feat: initial admin panel (Vue + Tailwind)

This commit is contained in:
2026-05-18 21:28:43 +03:30
commit 03f3994121
32 changed files with 3535 additions and 0 deletions
+37
View File
@@ -0,0 +1,37 @@
import { api } from './client';
export type User = {
id: string;
mobile: string;
fullName: string | null;
role: string;
isActive: boolean;
};
export type RequestOtpResponse = {
expiresInSeconds: number;
otpCode?: string;
};
export type VerifyOtpResponse = {
accessToken: string;
user: User;
};
export function requestOtp(mobile: string) {
return api<RequestOtpResponse>('/auth/request-otp', {
method: 'POST',
body: JSON.stringify({ mobile }),
});
}
export function verifyOtp(mobile: string, code: string) {
return api<VerifyOtpResponse>('/auth/verify-otp', {
method: 'POST',
body: JSON.stringify({ mobile, code }),
});
}
export function fetchMe(token: string) {
return api<User>('/auth/me', { token });
}
+58
View File
@@ -0,0 +1,58 @@
const baseUrl = import.meta.env.VITE_API_URL ?? 'http://localhost:3000';
export class ApiError extends Error {
constructor(
readonly status: number,
message: string,
) {
super(message);
this.name = 'ApiError';
}
}
type ApiOptions = RequestInit & {
token?: string;
};
const TOKEN_KEY = 'montime_admin_access_token';
export function getApiToken(): string | null {
return localStorage.getItem(TOKEN_KEY);
}
export async function api<T>(path: string, options: ApiOptions = {}): Promise<T> {
const token = options.token ?? getApiToken() ?? undefined;
const headers: Record<string, string> = {
'Content-Type': 'application/json',
...(options.headers as Record<string, string> | undefined),
};
if (token) {
headers.Authorization = `Bearer ${token}`;
}
const { token: _token, ...fetchOptions } = options;
const res = await fetch(`${baseUrl}${path}`, {
...fetchOptions,
headers,
});
if (!res.ok) {
const body = (await res.json().catch(() => null)) as
| { message?: string | string[] }
| null;
const msg = Array.isArray(body?.message)
? body.message.join(', ')
: body?.message ?? res.statusText;
throw new ApiError(res.status, msg);
}
if (res.status === 204) {
return undefined as T;
}
return res.json() as Promise<T>;
}
export { TOKEN_KEY };