diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..8eea8ee --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,4 @@ +{ + "recommendations": ["Vue.volar"], + "unwantedRecommendations": ["octref.vetur"] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..62f3b7b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "typescript.tsdk": "node_modules/typescript/lib", + "typescript.enablePromptUseWorkspaceTsdk": true, + "vetur.validation.script": false, + "vetur.validation.template": false, + "vetur.validation.style": false, + "vetur.useWorkspaceDependencies": true, + "vetur.ignoreProjectWarning": true, + "vetur.experimental.templateInterpolationService": false, + "vue.server.hybridMode": false +} diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..626baec --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + }, + "module": "ESNext", + "moduleResolution": "bundler", + "target": "ES2022", + "jsx": "preserve" + }, + "include": ["shims-vue.d.ts", "src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"], + "exclude": ["dist", "dist-admin", "node_modules"] +} diff --git a/shims-vue.d.ts b/shims-vue.d.ts new file mode 100644 index 0000000..aad3656 --- /dev/null +++ b/shims-vue.d.ts @@ -0,0 +1,5 @@ +declare module '*.vue' { + import type { DefineComponent } from 'vue'; + const component: DefineComponent; + export default component; +} diff --git a/src/api/client.ts b/src/api/client.ts index e1bb712..7865a42 100644 --- a/src/api/client.ts +++ b/src/api/client.ts @@ -20,6 +20,36 @@ export function getApiToken(): string | null { return localStorage.getItem('montime_access_token'); } +function resolveApiErrorMessage( + status: number, + statusText: string, + body: { message?: string | string[] } | null, +): string { + if (Array.isArray(body?.message)) { + const lines = body.message.map(String); + const timeErrors = lines.filter( + (l) => /startTime|endTime/.test(l) && /regular expression|HH:mm/i.test(l), + ); + if (timeErrors.length > 0) { + return 'فرمت ساعت نامعتبر است. لطفاً دوباره بازه را اضافه و ذخیره کنید.'; + } + return lines.join(' · '); + } + + const raw = typeof body?.message === 'string' ? body.message : statusText; + const genericServer = + status >= 500 && + (!raw || + raw === 'Internal Server Error' || + /^5\d\d/.test(String(raw))); + + if (genericServer) { + return 'ارتباط با سرور برقرار نشد. اتصال اینترنت را بررسی کنید یا چند لحظه بعد دوباره تلاش کنید.'; + } + + return raw || 'خطا در ارتباط با سرور'; +} + export async function api(path: string, options: ApiOptions = {}): Promise { const token = options.token ?? getApiToken() ?? undefined; const headers: Record = { @@ -33,10 +63,18 @@ export async function api(path: string, options: ApiOptions = {}): Promise const { token: _token, ...fetchOptions } = options; - const res = await fetch(`${baseUrl}${path}`, { - ...fetchOptions, - headers, - }); + let res: Response; + try { + res = await fetch(`${baseUrl}${path}`, { + ...fetchOptions, + headers, + }); + } catch { + throw new ApiError( + 0, + 'ارتباط با سرور برقرار نشد. اتصال اینترنت را بررسی کنید یا چند لحظه بعد دوباره تلاش کنید.', + ); + } logApiServerTiming(path, res); @@ -44,19 +82,7 @@ export async function api(path: string, options: ApiOptions = {}): Promise const body = (await res.json().catch(() => null)) as | { message?: string | string[] } | null; - let msg: string; - if (Array.isArray(body?.message)) { - const lines = body.message.map(String); - const timeErrors = lines.filter((l) => - /startTime|endTime/.test(l) && /regular expression|HH:mm/i.test(l), - ); - msg = - timeErrors.length > 0 - ? 'فرمت ساعت نامعتبر است. لطفاً دوباره بازه را اضافه و ذخیره کنید.' - : lines.join(' · '); - } else { - msg = body?.message ?? res.statusText; - } + const msg = resolveApiErrorMessage(res.status, res.statusText, body); throw new ApiError(res.status, msg); } diff --git a/src/components/owner/BusinessAvailabilityEditor.vue b/src/components/owner/BusinessAvailabilityEditor.vue index 1006062..b4f800e 100644 --- a/src/components/owner/BusinessAvailabilityEditor.vue +++ b/src/components/owner/BusinessAvailabilityEditor.vue @@ -27,9 +27,19 @@ مشتری فقط در بازه‌های تعریف‌شده می‌تواند رزرو کند. ابتدا حالت کلی را انتخاب کنید.

-

- {{ error }} -

+
+

{{ error }}

+ +

-

-