diff --git a/.claude/launch.json b/.claude/launch.json new file mode 100644 index 0000000..67f2ca8 --- /dev/null +++ b/.claude/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.0.1", + "configurations": [ + { + "name": "hr-appeals-web", + "runtimeExecutable": "npm", + "runtimeArgs": ["run", "dev"], + "port": 3000 + } + ] +} diff --git a/src/features/feedback/texts.ts b/src/features/feedback/texts.ts index 31fbc62..f033195 100644 --- a/src/features/feedback/texts.ts +++ b/src/features/feedback/texts.ts @@ -46,7 +46,8 @@ export const TEXTS = { required: 'Заполните поле', selectRequired: 'Выберите значение', tooShort: 'Слишком короткий ответ — опишите подробнее', - hasErrors: 'Проверьте выделенные поля — что-то заполнено не полностью.' + hasErrors: 'Проверьте выделенные поля — что-то заполнено не полностью.', + submitFailed: 'Не удалось отправить обращение. Попробуйте ещё раз позже.' }, thanks: { diff --git a/src/features/feedback/useFeedbackForm.ts b/src/features/feedback/useFeedbackForm.ts index 6bba609..0cd1816 100644 --- a/src/features/feedback/useFeedbackForm.ts +++ b/src/features/feedback/useFeedbackForm.ts @@ -18,8 +18,8 @@ export interface IFeedbackForm { /** * Состояние формы обращения: данные, валидация, отправка. - * Бэкенда пока нет — submit только имитирует отправку (см. TODO ниже), - * поэтому вся логика синхронная и живёт здесь, без сервисного слоя. + * Отправка идёт через nginx (/api/... -> hr-appeals-auth) — сервис сам + * меняет её на app-only токен и создаёт элемент в списке SharePoint. */ export function useFeedbackForm(): IFeedbackForm { const [data, setData] = useState(EMPTY_FEEDBACK); @@ -53,11 +53,24 @@ export function useFeedbackForm(): IFeedbackForm { setFormError(''); setStatus('submitting'); - // TODO: заменить на реальную отправку, когда определимся с бэкендом. - // Сейчас это заглушка по договорённости: делаем только UI. - // eslint-disable-next-line no-console - console.info('Обращение (заглушка отправки):', data); - setStatus('done'); + fetch('/api/feedback', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(data) + }) + .then(async (response) => { + if (!response.ok) { + const body = await response.json().catch(() => null); + throw new Error(body?.error ?? `Сервер вернул ${response.status}`); + } + setStatus('done'); + }) + .catch((error) => { + // eslint-disable-next-line no-console + console.error('Не удалось отправить обращение:', error); + setFormError(TEXTS.errors.submitFailed); + setStatus('idle'); + }); }, [data]); const reset = useCallback((): void => { diff --git a/vite.config.ts b/vite.config.ts index 6ef91fc..2342cbc 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -12,7 +12,16 @@ export default defineConfig({ }, server: { port: 3000, - open: true + open: true, + // Как nginx в проде (nginx.conf): /api/... идёт на hr-appeals-auth без + // префикса. Локально сервис запускаем на 3100 — порт 3000 занят самим vite. + proxy: { + '/api': { + target: 'http://localhost:3100', + changeOrigin: true, + rewrite: (path) => path.replace(/^\/api/, '') + } + } }, test: { // globals: true — describe/it/expect доступны без импорта, как привычно по Jest.