feat: add insurance and location management

- Introduced InsuranceType enum for insurance categorization.
- Created InsuranceRepository for managing insurance entities.
- Developed LocationController for handling provinces and cities, including CRUD operations.
- Implemented City and Province entities with necessary fields and relationships.
- Added CityRepository and ProvinceRepository for database interactions.
- Established Specialty management with SpecialtyController, including CRUD operations.
- Created Specialty and Tag entities with appropriate fields and relationships.
- Implemented TagController for managing tags, including CRUD operations.
- Added TagRepository for database interactions with tags.
This commit is contained in:
hamed
2026-06-10 14:22:26 +03:30
parent 4b8504df91
commit 5066fcbd91
36 changed files with 2937 additions and 1241 deletions
File diff suppressed because it is too large Load Diff
@@ -4,8 +4,8 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { PencilIcon, TrashIcon, CheckCircleIcon, XCircleIcon } from '@heroicons/react/24/outline';
import { toast } from 'sonner';
import { api } from '../lib/api';
import type { ApiResponse } from '../lib/api';
import type { Representation, Category } from '../types';
import type { ApiResponse, PaginatedResponse } from '../lib/api';
import type { Representation, City } from '../types';
import { formatDate, formatNumber } from '../lib/utils';
import PageHeader from '../components/ui/PageHeader';
import { ActiveBadge } from '../components/ui/StatusBadge';
@@ -30,11 +30,11 @@ export default function RepresentationDetailPage() {
const [formData, setFormData] = useState({ full_name: '', city_id: '', mobile_number: '', commission_percent: '' });
const citiesQuery = useQuery({
queryKey: ['categories', 'city'],
queryFn: () => api.get<ApiResponse<{ data: Category[] }>>('/api/v1/categorys/city'),
queryKey: ['cities-select'],
queryFn: () => api.get<PaginatedResponse<City>>('/api/v1/admin/cities?limit=200'),
staleTime: 5 * 60_000,
});
const cities: Category[] = (citiesQuery.data?.data as any)?.data ?? [];
const cities: City[] = citiesQuery.data?.data ?? [];
const { data, isLoading } = useQuery({
queryKey: ['representation', uuid],
@@ -251,7 +251,7 @@ export default function RepresentationDetailPage() {
>
<option value="">انتخاب شهر</option>
{cities.map((c) => (
<option key={c.id} value={String(c.id)}>{c.label}</option>
<option key={c.id} value={String(c.id)}>{c.name}</option>
))}
</select>
</div>
+5 -5
View File
@@ -8,7 +8,7 @@ import { z } from 'zod';
import { toast } from 'sonner';
import { api } from '../lib/api';
import type { ApiResponse, PaginatedResponse } from '../lib/api';
import type { Representation, Category } from '../types';
import type { Representation, City } from '../types';
import { formatDate, formatRial, formatNumber } from '../lib/utils';
import PageHeader from '../components/ui/PageHeader';
import DataTable, { Column } from '../components/ui/DataTable';
@@ -38,12 +38,12 @@ export default function RepresentationsPage() {
// Load city list for filter and form
const citiesQuery = useQuery({
queryKey: ['categories', 'city'],
queryFn: () => api.get<ApiResponse<{ data: Category[] }>>('/api/v1/categorys/city'),
queryKey: ['cities-select'],
queryFn: () => api.get<PaginatedResponse<City>>('/api/v1/admin/cities?limit=200'),
staleTime: 5 * 60_000,
});
const cities: Category[] = (citiesQuery.data?.data as any)?.data ?? [];
const cityOptions = cities.map((c) => ({ value: c.id, label: c.label }));
const cities: City[] = citiesQuery.data?.data ?? [];
const cityOptions = cities.map((c) => ({ value: c.id, label: c.name }));
const { data, isLoading } = useQuery({
queryKey: ['representations', page, search, cityFilter],
+51 -19
View File
@@ -165,28 +165,21 @@ export interface SmsLog {
created_at: string;
}
export type CategoryBundle =
| 'state'
| 'city'
| 'specially_doctor'
| 'doctor_services'
| 'insurance_type'
| 'supplementary_insurance'
| 'tag';
export interface Category {
export interface Province {
id: number;
uuid: string;
label: string;
bundle: CategoryBundle;
name: string;
status: number;
weight: number;
parent_id?: number | null;
title?: string | null;
logo_id?: number | null;
// insurance-specific
logo_url?: string | null;
// city-specific
}
export interface City {
id: number;
uuid: string;
name: string;
status: number;
weight: number;
province_id: number | null;
representation_id?: number | null;
contact_phone?: string | null;
email?: string | null;
@@ -195,8 +188,45 @@ export interface Category {
domain?: string | null;
keywords?: string | null;
footer_description?: string | null;
footer_disclaimer?: string | null;
social_media?: Record<string, string> | null;
logo_url?: string | null;
}
export interface SpecialtyFull {
id: number;
uuid: string;
name: string;
slug: string;
status: number;
weight: number;
parent_id: number | null;
}
export interface DoctorService {
id: number;
uuid: string;
name: string;
slug: string;
status: number;
weight: number;
specialty_id: number | null;
}
export interface Insurance {
id: number;
uuid: string;
name: string;
type: 'basic' | 'supplementary';
logo_url: string | null;
status: number;
}
export interface Tag {
id: number;
uuid: string;
name: string;
slug: string;
status: number;
}
export interface Blog {
@@ -251,6 +281,8 @@ export interface SecretaryPermissions {
}
export interface Specialty {
id?: number;
uuid: string;
name: string;
slug?: string;
}