diff --git a/.vscode/settings.json b/.vscode/settings.json index 62f3b7b..a57e7af 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,11 +1,5 @@ -{ - "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 -} + "vue.server.hybridMode": false \ No newline at end of file diff --git a/index.html b/index.html index edbfc46..3bcf9b6 100644 --- a/index.html +++ b/index.html @@ -2,20 +2,11 @@ + - منتایم - - - - - - - + منتایم
diff --git a/public/brand/login-hero-default.png b/public/brand/login-hero-default.png new file mode 100644 index 0000000..2b52b74 Binary files /dev/null and b/public/brand/login-hero-default.png differ diff --git a/public/font/iranYekanX/IRANYekanXFaNum-Black.woff2 b/public/font/iranYekanX/IRANYekanXFaNum-Black.woff2 new file mode 100644 index 0000000..53ae709 Binary files /dev/null and b/public/font/iranYekanX/IRANYekanXFaNum-Black.woff2 differ diff --git a/public/font/iranYekanX/IRANYekanXFaNum-Bold.woff2 b/public/font/iranYekanX/IRANYekanXFaNum-Bold.woff2 new file mode 100644 index 0000000..8f5b1cb Binary files /dev/null and b/public/font/iranYekanX/IRANYekanXFaNum-Bold.woff2 differ diff --git a/public/font/iranYekanX/IRANYekanXFaNum-ExtraBlack.woff2 b/public/font/iranYekanX/IRANYekanXFaNum-ExtraBlack.woff2 new file mode 100644 index 0000000..8faf1a4 Binary files /dev/null and b/public/font/iranYekanX/IRANYekanXFaNum-ExtraBlack.woff2 differ diff --git a/public/font/iranYekanX/IRANYekanXFaNum-Light.woff2 b/public/font/iranYekanX/IRANYekanXFaNum-Light.woff2 new file mode 100644 index 0000000..bae7f99 Binary files /dev/null and b/public/font/iranYekanX/IRANYekanXFaNum-Light.woff2 differ diff --git a/public/font/iranYekanX/IRANYekanXFaNum-Regular.woff2 b/public/font/iranYekanX/IRANYekanXFaNum-Regular.woff2 new file mode 100644 index 0000000..6467890 Binary files /dev/null and b/public/font/iranYekanX/IRANYekanXFaNum-Regular.woff2 differ diff --git a/public/landing/hero-car-wash.jpg b/public/landing/hero-car-wash.jpg new file mode 100644 index 0000000..331f800 --- /dev/null +++ b/public/landing/hero-car-wash.jpg @@ -0,0 +1 @@ +404 \ No newline at end of file diff --git a/public/landing/hero-reference.png b/public/landing/hero-reference.png new file mode 100644 index 0000000..3f3a748 Binary files /dev/null and b/public/landing/hero-reference.png differ diff --git a/src/App.vue b/src/App.vue index 39f5614..8d10130 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,5 +1,8 @@ - + diff --git a/src/api/platform-settings.ts b/src/api/platform-settings.ts new file mode 100644 index 0000000..fb26850 --- /dev/null +++ b/src/api/platform-settings.ts @@ -0,0 +1,9 @@ +import { api } from './client'; + +export type PlatformSettings = { + loginHeroImageUrl: string | null; +}; + +export function fetchPlatformSettings() { + return api('/platform-settings'); +} diff --git a/src/api/types.ts b/src/api/types.ts index 6d9755d..65f0e9f 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -1,3 +1,5 @@ +export type BookingStatus = 'pending' | 'confirmed' | 'cancelled' | 'completed'; + export type BusinessCategory = { id: string; slug: string; @@ -8,94 +10,68 @@ export type BusinessCategory = { export type BusinessImage = { id: string; url: string; - isCover: boolean; + originalName?: string | null; sortOrder: number; + isCover: boolean; }; export type Business = { id: string; title: string; + subtitle?: string | null; slug: string; - description: string | null; - contactPhone: string | null; - address: string | null; - latitude: number | null; - longitude: number | null; + description?: string | null; + address?: string | null; + phone?: string | null; isActive: boolean; - serviceClassesEnabled?: boolean; - concurrentCapacity?: number; - slotStepMinutes?: number; categoryId: string; - ownerId?: string | null; category?: BusinessCategory; images?: BusinessImage[]; -}; - -export type BusinessRegistrationStatus = 'pending' | 'approved' | 'rejected'; - -export type BusinessRegistrationRequest = { - id: string; - applicantId: string; - status: BusinessRegistrationStatus; - title: string; - slug: string; - categoryId: string; - description: string | null; - contactPhone: string | null; - address: string | null; - adminNote: string | null; - businessId: string | null; - createdAt: string; - category?: BusinessCategory; - applicant?: { id: string; mobile: string; fullName: string | null }; -}; - -export type CreateBusinessRegistrationPayload = { - title: string; - categoryId: string; - slug?: string; - description?: string; - contactPhone?: string; - address?: string; + concurrentCapacity?: number; + slotStepMinutes?: number; + serviceClassesEnabled?: boolean; + createdAt?: string; + updatedAt?: string; }; export type UpdateBusinessPayload = { title?: string; + subtitle?: string | null; description?: string | null; - contactPhone?: string | null; address?: string | null; - latitude?: number | null; - longitude?: number | null; - serviceClassesEnabled?: boolean; + phone?: string | null; + isActive?: boolean; concurrentCapacity?: number; slotStepMinutes?: number; -}; - -export type BusinessServiceClass = { - id: string; - businessId: string; - title: string; - description: string | null; - sortOrder: number; - isActive: boolean; -}; - -export type BusinessServiceClassPrice = { - id: string; - businessServiceId: string; - classId: string; - price: string; - durationMinutes: number | null; - isActive: boolean; - serviceClass?: BusinessServiceClass; + serviceClassesEnabled?: boolean; }; export type ServiceTemplate = { id: string; title: string; slug: string; - description: string | null; - defaultDurationMinutes: number | null; + description?: string | null; + defaultDurationMinutes?: number | null; + isActive: boolean; + categoryId: string; +}; + +export type BusinessServiceClassPrice = { + id: string; + classId: string; + price: string; + durationMinutes?: number | null; + isActive: boolean; +}; + +export type BusinessServiceClass = { + id: string; + businessId: string; + title: string; + description?: string | null; + sortOrder: number; + isActive: boolean; + classPrices?: BusinessServiceClassPrice[]; }; export type BusinessService = { @@ -103,43 +79,12 @@ export type BusinessService = { businessId: string; serviceTemplateId: string; price: string; - durationMinutes: number | null; + durationMinutes?: number | null; isActive: boolean; serviceTemplate?: ServiceTemplate; classPrices?: BusinessServiceClassPrice[]; }; -export type BusinessServiceRequestStatus = 'pending' | 'approved' | 'rejected'; - -export type BusinessServiceRequest = { - id: string; - businessId: string; - requestedById: string; - status: BusinessServiceRequestStatus; - title: string; - description: string | null; - price: string; - durationMinutes: number | null; - adminNote: string | null; - serviceTemplateId: string | null; - businessServiceId: string | null; - createdAt?: string; - business?: Business; -}; - -export type CreateBusinessServiceRequestPayload = { - title: string; - description?: string; - price: number; - durationMinutes?: number; -}; - -export type BookingStatus = - | 'pending' - | 'confirmed' - | 'cancelled' - | 'completed'; - export type Booking = { id: string; customerId: string; @@ -149,15 +94,49 @@ export type Booking = { bookingGroupId?: string | null; scheduledAt: string; status: BookingStatus; - note: string | null; - createdAt?: string; - updatedAt?: string; - customer?: { - id: string; - mobile: string; - fullName: string | null; - }; + note?: string | null; business?: Business; businessService?: BusinessService; serviceClass?: BusinessServiceClass; + customer?: { + id: string; + mobile: string; + fullName?: string | null; + }; +}; + +export type BusinessRegistrationRequest = { + id: string; + status: string; + businessTitle: string; + categoryId: string; + ownerMobile: string; + createdAt: string; +}; + +export type CreateBusinessRegistrationPayload = { + businessTitle: string; + categoryId: string; + description?: string; + address?: string; + phone?: string; +}; + +export type BusinessServiceRequest = { + id: string; + businessId: string; + status: string; + title: string; + description?: string | null; + price: string; + durationMinutes?: number | null; + createdAt: string; +}; + +export type CreateBusinessServiceRequestPayload = { + businessId: string; + title: string; + description?: string; + price: number; + durationMinutes?: number; }; diff --git a/src/assets/fonts/IRANYekanXFaNum-Black.woff2 b/src/assets/fonts/IRANYekanXFaNum-Black.woff2 new file mode 100644 index 0000000..53ae709 Binary files /dev/null and b/src/assets/fonts/IRANYekanXFaNum-Black.woff2 differ diff --git a/src/assets/fonts/IRANYekanXFaNum-Bold.woff2 b/src/assets/fonts/IRANYekanXFaNum-Bold.woff2 new file mode 100644 index 0000000..8f5b1cb Binary files /dev/null and b/src/assets/fonts/IRANYekanXFaNum-Bold.woff2 differ diff --git a/src/assets/fonts/IRANYekanXFaNum-ExtraBlack.woff2 b/src/assets/fonts/IRANYekanXFaNum-ExtraBlack.woff2 new file mode 100644 index 0000000..8faf1a4 Binary files /dev/null and b/src/assets/fonts/IRANYekanXFaNum-ExtraBlack.woff2 differ diff --git a/src/assets/fonts/IRANYekanXFaNum-Light.woff2 b/src/assets/fonts/IRANYekanXFaNum-Light.woff2 new file mode 100644 index 0000000..bae7f99 Binary files /dev/null and b/src/assets/fonts/IRANYekanXFaNum-Light.woff2 differ diff --git a/src/assets/fonts/IRANYekanXFaNum-Regular.woff2 b/src/assets/fonts/IRANYekanXFaNum-Regular.woff2 new file mode 100644 index 0000000..6467890 Binary files /dev/null and b/src/assets/fonts/IRANYekanXFaNum-Regular.woff2 differ diff --git a/src/components/auth/DesktopLoginHeroPanel.vue b/src/components/auth/DesktopLoginHeroPanel.vue new file mode 100644 index 0000000..341ee16 --- /dev/null +++ b/src/components/auth/DesktopLoginHeroPanel.vue @@ -0,0 +1,161 @@ + + + + + diff --git a/src/components/booking/BookingItem.vue b/src/components/booking/BookingItem.vue index bdfd3c1..cf6e4fb 100644 --- a/src/components/booking/BookingItem.vue +++ b/src/components/booking/BookingItem.vue @@ -1,45 +1,71 @@ diff --git a/src/components/home/HomeBusinessCard.vue b/src/components/home/HomeBusinessCard.vue new file mode 100644 index 0000000..8b3da26 --- /dev/null +++ b/src/components/home/HomeBusinessCard.vue @@ -0,0 +1,62 @@ + + + diff --git a/src/components/home/HomeBusinessList.vue b/src/components/home/HomeBusinessList.vue new file mode 100644 index 0000000..a35f3bc --- /dev/null +++ b/src/components/home/HomeBusinessList.vue @@ -0,0 +1,52 @@ + + + diff --git a/src/components/home/HomeCustomerClubSection.vue b/src/components/home/HomeCustomerClubSection.vue new file mode 100644 index 0000000..70969a7 --- /dev/null +++ b/src/components/home/HomeCustomerClubSection.vue @@ -0,0 +1,64 @@ + + + diff --git a/src/components/home/HomeDiscoveryContent.vue b/src/components/home/HomeDiscoveryContent.vue new file mode 100644 index 0000000..a1e40cc --- /dev/null +++ b/src/components/home/HomeDiscoveryContent.vue @@ -0,0 +1,113 @@ + + + diff --git a/src/components/home/HomeFeatureBar.vue b/src/components/home/HomeFeatureBar.vue new file mode 100644 index 0000000..331c20d --- /dev/null +++ b/src/components/home/HomeFeatureBar.vue @@ -0,0 +1,39 @@ + + + diff --git a/src/components/home/HomeHeroSection.vue b/src/components/home/HomeHeroSection.vue new file mode 100644 index 0000000..3fe48a6 --- /dev/null +++ b/src/components/home/HomeHeroSection.vue @@ -0,0 +1,135 @@ + + + diff --git a/src/components/home/HomeSearchAndFilter.vue b/src/components/home/HomeSearchAndFilter.vue new file mode 100644 index 0000000..018cd9a --- /dev/null +++ b/src/components/home/HomeSearchAndFilter.vue @@ -0,0 +1,52 @@ + + + diff --git a/src/components/home/HomeSectionTitle.vue b/src/components/home/HomeSectionTitle.vue new file mode 100644 index 0000000..927d9a5 --- /dev/null +++ b/src/components/home/HomeSectionTitle.vue @@ -0,0 +1,23 @@ + + + diff --git a/src/components/home/HomeServiceCard.vue b/src/components/home/HomeServiceCard.vue new file mode 100644 index 0000000..92d4564 --- /dev/null +++ b/src/components/home/HomeServiceCard.vue @@ -0,0 +1,47 @@ + + + diff --git a/src/components/home/HomeServicesSection.vue b/src/components/home/HomeServicesSection.vue new file mode 100644 index 0000000..65634b1 --- /dev/null +++ b/src/components/home/HomeServicesSection.vue @@ -0,0 +1,80 @@ + + + diff --git a/src/components/landing/DesktopDiscoveryPage.vue b/src/components/landing/DesktopDiscoveryPage.vue new file mode 100644 index 0000000..d07657e --- /dev/null +++ b/src/components/landing/DesktopDiscoveryPage.vue @@ -0,0 +1,10 @@ + + + diff --git a/src/components/landing/DesktopLandingPage.vue b/src/components/landing/DesktopLandingPage.vue new file mode 100644 index 0000000..6c2fd3e --- /dev/null +++ b/src/components/landing/DesktopLandingPage.vue @@ -0,0 +1,53 @@ + + + diff --git a/src/components/profile/ProfileDropdown.vue b/src/components/profile/ProfileDropdown.vue new file mode 100644 index 0000000..e3ad9e7 --- /dev/null +++ b/src/components/profile/ProfileDropdown.vue @@ -0,0 +1,120 @@ + + + diff --git a/src/components/profile/ProfileModal.vue b/src/components/profile/ProfileModal.vue new file mode 100644 index 0000000..8e1cccc --- /dev/null +++ b/src/components/profile/ProfileModal.vue @@ -0,0 +1,13 @@ + + + diff --git a/src/components/profile/ProfilePanel.vue b/src/components/profile/ProfilePanel.vue new file mode 100644 index 0000000..a3491c3 --- /dev/null +++ b/src/components/profile/ProfilePanel.vue @@ -0,0 +1,158 @@ + + + diff --git a/src/components/shell/AppShell.vue b/src/components/shell/AppShell.vue index 555785a..953a35a 100644 --- a/src/components/shell/AppShell.vue +++ b/src/components/shell/AppShell.vue @@ -1,7 +1,9 @@ diff --git a/src/components/shell/AuthHeaderAction.vue b/src/components/shell/AuthHeaderAction.vue new file mode 100644 index 0000000..ade6f0f --- /dev/null +++ b/src/components/shell/AuthHeaderAction.vue @@ -0,0 +1,43 @@ + + + diff --git a/src/components/shell/DesktopSiteHeader.vue b/src/components/shell/DesktopSiteHeader.vue new file mode 100644 index 0000000..5d3aa79 --- /dev/null +++ b/src/components/shell/DesktopSiteHeader.vue @@ -0,0 +1,46 @@ + + + diff --git a/src/components/shell/FloatingChatButton.vue b/src/components/shell/FloatingChatButton.vue new file mode 100644 index 0000000..7070ed6 --- /dev/null +++ b/src/components/shell/FloatingChatButton.vue @@ -0,0 +1,19 @@ + + + diff --git a/src/components/shell/SiteFooter.vue b/src/components/shell/SiteFooter.vue new file mode 100644 index 0000000..474efc0 --- /dev/null +++ b/src/components/shell/SiteFooter.vue @@ -0,0 +1,103 @@ + + + diff --git a/src/components/ui/AppButton.vue b/src/components/ui/AppButton.vue index 57e68d0..5a32616 100644 --- a/src/components/ui/AppButton.vue +++ b/src/components/ui/AppButton.vue @@ -14,7 +14,7 @@ import { computed } from 'vue'; const props = withDefaults( defineProps<{ - variant?: 'primary' | 'ghost' | 'danger'; + variant?: 'primary' | 'ghost' | 'danger' | 'outline'; type?: 'button' | 'submit'; disabled?: boolean; loading?: boolean; @@ -28,6 +28,8 @@ const variantClass = computed(() => { return 'bg-transparent text-muted hover:bg-surface-3 hover:text-text'; case 'danger': return 'bg-red-500/15 text-red-600 dark:text-red-400 hover:bg-red-500/25'; + case 'outline': + return 'border border-border bg-white text-text hover:bg-surface-2 dark:bg-surface dark:hover:bg-surface-2'; default: return 'bg-brand-600 text-white hover:bg-brand-700'; } diff --git a/src/components/ui/AppIcon.vue b/src/components/ui/AppIcon.vue index 7750e68..f13899a 100644 --- a/src/components/ui/AppIcon.vue +++ b/src/components/ui/AppIcon.vue @@ -19,31 +19,46 @@ export default defineComponent({ import type { Component } from 'vue'; import { computed } from 'vue'; import { + Armchair, ArrowRight, + BadgePercent, Calendar, Camera, + Car, ChevronDown, ChevronUp, CircleCheck, Clock, ClipboardList, + Cog, + Droplet, Eye, EyeOff, FolderOpen, + Headphones, Home, LayoutDashboard, MapPin, + MessageCircle, Moon, - Plus, + Phone, Pencil, + Plus, Search, + Send, + Settings, + Shield, + Sparkles, + SprayCan, Store, Sun, Trash2, User, UserCog, Users, + Wind, X, + Zap, } from '@lucide/vue'; import type { AppIconName } from '@/types/icons'; @@ -73,12 +88,27 @@ const iconMap: Record = { 'user-cog': UserCog, eye: Eye, 'eye-off': EyeOff, + shield: Shield, + settings: Settings, + droplet: Droplet, + headphones: Headphones, + 'badge-percent': BadgePercent, + 'message-circle': MessageCircle, + phone: Phone, + send: Send, + sparkles: Sparkles, + armchair: Armchair, + wind: Wind, + car: Car, + cog: Cog, + spray: SprayCan, + zap: Zap, }; const props = withDefaults( defineProps<{ name: AppIconName; - size?: 'sm' | 'md' | 'lg'; + size?: 'sm' | 'md' | 'lg' | 'xl'; strokeWidth?: number; }>(), { @@ -88,7 +118,12 @@ const props = withDefaults( ); const sizeClass = computed(() => { - const map = { sm: 'h-4 w-4', md: 'h-5 w-5', lg: 'h-6 w-6' } as const; + const map = { + sm: 'h-4 w-4', + md: 'h-5 w-5', + lg: 'h-6 w-6', + xl: 'h-8 w-8', + } as const; return map[props.size]; }); diff --git a/src/components/ui/AppModal.vue b/src/components/ui/AppModal.vue index 4b9717a..e8b8d1a 100644 --- a/src/components/ui/AppModal.vue +++ b/src/components/ui/AppModal.vue @@ -30,13 +30,20 @@ diff --git a/src/components/ui/AppSelect.vue b/src/components/ui/AppSelect.vue new file mode 100644 index 0000000..968e088 --- /dev/null +++ b/src/components/ui/AppSelect.vue @@ -0,0 +1,36 @@ + + + diff --git a/src/components/ui/AppTextarea.vue b/src/components/ui/AppTextarea.vue new file mode 100644 index 0000000..075fdfa --- /dev/null +++ b/src/components/ui/AppTextarea.vue @@ -0,0 +1,29 @@ +