@burdenoff/microfe-proserviceworld 2026.625.1 → 2026.626.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/proserviceworld/pages/AdminReportsPage.js +102 -68
- package/dist/proserviceworld/pages/AdminReportsPage.js.map +1 -1
- package/dist/proserviceworld/pages/AdminUsersPage.js +37 -33
- package/dist/proserviceworld/pages/AdminUsersPage.js.map +1 -1
- package/dist/proserviceworld/pages/AnalyticsPage.js +66 -36
- package/dist/proserviceworld/pages/AnalyticsPage.js.map +1 -1
- package/dist/proserviceworld/pages/BookingsPage.js +52 -48
- package/dist/proserviceworld/pages/BookingsPage.js.map +1 -1
- package/dist/proserviceworld/pages/DashboardPage.js +275 -243
- package/dist/proserviceworld/pages/DashboardPage.js.map +1 -1
- package/dist/proserviceworld/pages/DocumentsPage.js +204 -200
- package/dist/proserviceworld/pages/DocumentsPage.js.map +1 -1
- package/dist/proserviceworld/pages/MessagesPage.js +200 -186
- package/dist/proserviceworld/pages/MessagesPage.js.map +1 -1
- package/dist/proserviceworld/pages/PaymentsPage.js +70 -39
- package/dist/proserviceworld/pages/PaymentsPage.js.map +1 -1
- package/dist/proserviceworld/pages/ProjectsPage.js +510 -477
- package/dist/proserviceworld/pages/ProjectsPage.js.map +1 -1
- package/dist/proserviceworld/pages/ProviderDetailPage.js +71 -41
- package/dist/proserviceworld/pages/ProviderDetailPage.js.map +1 -1
- package/dist/proserviceworld/pages/ProvidersPage.js +62 -58
- package/dist/proserviceworld/pages/ProvidersPage.js.map +1 -1
- package/dist/proserviceworld/pages/ReviewsPage.js +80 -63
- package/dist/proserviceworld/pages/ReviewsPage.js.map +1 -1
- package/dist/proserviceworld/pages/ServiceDetailsPage.js +118 -83
- package/dist/proserviceworld/pages/ServiceDetailsPage.js.map +1 -1
- package/dist/proserviceworld/pages/ServicesPage.js +134 -130
- package/dist/proserviceworld/pages/ServicesPage.js.map +1 -1
- package/dist/proserviceworld/pages/VerificationPage.js +23 -19
- package/dist/proserviceworld/pages/VerificationPage.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProviderDetailPage.js","names":[],"sources":["../../../src/proserviceworld/pages/ProviderDetailPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport {\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Button,\n Badge,\n GlassCard,\n EmphasisPanel,\n CTAOverflowMenu,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\nimport {\n Star,\n CheckCircle,\n MapPin,\n Briefcase,\n Award,\n MessageSquare,\n Share2,\n Calendar,\n Users,\n} from 'lucide-react';\nimport { useProServiceWorldStore } from '../store/proserviceworldStore';\n\ntype DetailTab = 'overview' | 'services' | 'reviews';\n\nexport function ProviderDetailPage() {\n const { providers, services, reviews } = useProServiceWorldStore();\n const [activeTab, setActiveTab] = useState<DetailTab>('overview');\n\n const provider = providers[0];\n const providerServices = services.filter((s) => s.providerId === provider.id);\n const providerReviews = reviews.filter((r) => r.providerName === provider.name);\n\n const tabs: { id: DetailTab; label: string }[] = [\n { id: 'overview', label: 'Overview' },\n { id: 'services', label: `Services (${providerServices.length})` },\n { id: 'reviews', label: `Reviews (${providerReviews.length})` },\n ];\n\n return (\n <div className=\"space-y-6 p-6 bg-bg-canvas min-h-screen\">\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2\">\n {/* Provider hero — the single emphasised zone for this page */}\n <EmphasisPanel className=\"relative overflow-hidden\">\n <div className=\"flex items-start space-x-6\">\n <div className=\"h-20 w-20 rounded-full bg-accent-soft flex items-center justify-center shrink-0\">\n <span className=\"text-text-primary text-2xl font-bold\">\n {provider.name.charAt(0)}\n {provider.name.split(' ')[1]?.charAt(0)}\n </span>\n </div>\n <div className=\"flex-1\">\n <div className=\"flex items-center gap-3 mb-1\">\n <h1 className=\"text-2xl font-bold tracking-tight\">{provider.businessName}</h1>\n {provider.verified && (\n <Badge variant=\"default\">\n <CheckCircle className=\"h-3 w-3 mr-1\" />\n Verified\n </Badge>\n )}\n </div>\n <p className=\"text-text-secondary mb-2\">{provider.name}</p>\n <p className=\"text-sm text-text-secondary mb-4\">{provider.tagline}</p>\n <div className=\"flex flex-wrap items-center gap-4 text-sm\">\n <div className=\"flex items-center\">\n <Star className=\"h-4 w-4 text-status-warning-text fill-current mr-1\" />\n <span className=\"font-medium tabular-nums\">{provider.rating.toFixed(1)}</span>\n <span className=\"text-text-secondary ml-1\">\n ({provider.totalReviews} reviews)\n </span>\n </div>\n <div className=\"flex items-center text-text-secondary\">\n <MapPin className=\"h-4 w-4 mr-1\" />\n {provider.location}\n </div>\n <div className=\"flex items-center text-text-secondary\">\n <Briefcase className=\"h-4 w-4 mr-1\" />\n {provider.yearsExperience} years\n </div>\n </div>\n </div>\n </div>\n </EmphasisPanel>\n </div>\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam\">\n <CardContent className=\"p-6 space-y-4\">\n <div className=\"text-center\">\n <div className=\"text-2xl font-bold tabular-nums\">${provider.hourlyRate}/hr</div>\n <p className=\"text-sm text-text-secondary\">Starting rate</p>\n </div>\n <Button className=\"w-full\">\n <MessageSquare className=\"h-4 w-4 mr-2\" />\n Contact Provider\n </Button>\n <CTAOverflowMenu\n className=\"justify-center\"\n actions={[\n {\n label: 'Book Consultation',\n onSelect: () => {},\n icon: <Calendar className=\"h-4 w-4\" />,\n },\n {\n label: 'Share Profile',\n onSelect: () => {},\n icon: <Share2 className=\"h-4 w-4\" />,\n },\n ]}\n />\n <div className=\"grid grid-cols-2 gap-4 text-sm pt-4 border-t border-border-seam\">\n <div className=\"text-center p-3 bg-bg-sunken rounded\">\n <div className=\"font-semibold tabular-nums\">{provider.responseTime}h</div>\n <div className=\"text-text-secondary\">Response</div>\n </div>\n <div className=\"text-center p-3 bg-bg-sunken rounded\">\n <div className=\"font-semibold tabular-nums\">{provider.completionRate}%</div>\n <div className=\"text-text-secondary\">Completion</div>\n </div>\n <div className=\"text-center p-3 bg-bg-sunken rounded\">\n <div className=\"font-semibold tabular-nums\">{provider.projectsCompleted}</div>\n <div className=\"text-text-secondary\">Projects</div>\n </div>\n <div className=\"text-center p-3 bg-bg-sunken rounded\">\n <div className=\"font-semibold tabular-nums\">{provider.activeProjects}</div>\n <div className=\"text-text-secondary\">Active</div>\n </div>\n </div>\n </CardContent>\n </GlassCard>\n </div>\n\n <div className=\"border-b border-border-seam\">\n <nav className=\"-mb-px flex space-x-8\">\n {tabs.map((tab) => (\n <button\n key={tab.id}\n onClick={() => setActiveTab(tab.id)}\n aria-pressed={activeTab === tab.id}\n className={`py-3 px-1 border-b-2 font-medium text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus ${activeTab === tab.id ? 'border-accent-blue text-accent-blue' : 'border-transparent text-text-secondary hover:text-text-primary'}`}\n >\n {tab.label}\n </button>\n ))}\n </nav>\n </div>\n\n {activeTab === 'overview' && (\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-6\">\n <Card className=\"border-border-seam\">\n <CardHeader>\n <CardTitle>About</CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-text-secondary\">{provider.description}</p>\n </CardContent>\n </Card>\n <Card className=\"border-border-seam\">\n <CardHeader>\n <CardTitle>Specializations</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"flex flex-wrap gap-2\">\n {provider.specializations.map((s) => (\n <Badge key={s} variant=\"secondary\">\n {s}\n </Badge>\n ))}\n </div>\n </CardContent>\n </Card>\n <Card className=\"border-border-seam\">\n <CardHeader>\n <CardTitle>Certifications</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {provider.certifications.map((c) => (\n <div key={c} className=\"flex items-center gap-3\">\n <Award className=\"h-5 w-5 text-accent-blue\" />\n <span>{c}</span>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n <div className=\"space-y-6\">\n <Card className=\"border-border-seam\">\n <CardHeader>\n <CardTitle>Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3 text-sm\">\n <div className=\"flex justify-between\">\n <span className=\"text-text-secondary\">Location</span>\n <span>{provider.location}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-text-secondary\">Languages</span>\n <span>{provider.languages.join(', ')}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-text-secondary\">Member Since</span>\n <span>\n {new Date(provider.joinedDate).toLocaleDateString('en-US', {\n month: 'short',\n year: 'numeric',\n })}\n </span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-text-secondary\">Category</span>\n <span className=\"capitalize\">{provider.category.replace('_', ' ')}</span>\n </div>\n </CardContent>\n </Card>\n <Card className=\"border-border-seam\">\n <CardHeader>\n <CardTitle>Similar Providers</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {providers\n .filter((p) => p.id !== provider.id)\n .slice(0, 3)\n .map((p) => (\n <div key={p.id} className=\"flex items-center gap-3\">\n <div className=\"h-8 w-8 rounded-full bg-bg-sunken flex items-center justify-center text-xs font-medium\">\n {p.name.charAt(0)}\n </div>\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium truncate\">{p.businessName}</p>\n <p className=\"text-xs text-text-secondary\">{p.specializations[0]}</p>\n </div>\n <div className=\"flex items-center text-xs\">\n <Star className=\"h-3 w-3 text-status-warning-text fill-current mr-0.5\" />\n <span className=\"tabular-nums\">{p.rating.toFixed(1)}</span>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n </div>\n )}\n\n {activeTab === 'services' && (\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6\">\n {providerServices.length > 0 ? (\n providerServices.map((svc) => (\n <GlassCard\n key={svc.id}\n treatment=\"plain\"\n className=\"border-border-seam transition-shadow hover:shadow-pop\"\n >\n <CardContent className=\"p-6\">\n <h3 className=\"font-semibold mb-2\">{svc.title}</h3>\n <p className=\"text-sm text-text-secondary mb-3 line-clamp-2\">\n {svc.description}\n </p>\n <div className=\"flex flex-wrap gap-1 mb-3\">\n {svc.tags.slice(0, 3).map((t) => (\n <Badge key={t} variant=\"outline\" className=\"text-xs\">\n {t}\n </Badge>\n ))}\n </div>\n <div className=\"flex justify-between border-t border-border-seam pt-3\">\n <span className=\"font-medium tabular-nums\">\n From ${svc.pricing.basePrice.toLocaleString()}\n </span>\n <span className=\"text-sm text-text-secondary tabular-nums\">\n {svc.duration.typical} days\n </span>\n </div>\n <Button variant=\"outline\" size=\"sm\" className=\"w-full mt-3\">\n View Details\n </Button>\n </CardContent>\n </GlassCard>\n ))\n ) : (\n <div className=\"col-span-full\">\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No services listed yet\"\n description=\"This provider has not published any services.\"\n />\n </div>\n )}\n </div>\n )}\n\n {activeTab === 'reviews' && (\n <div className=\"space-y-4\">\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardContent className=\"p-6\">\n <div className=\"flex flex-col gap-8 sm:flex-row sm:items-center\">\n <div className=\"text-center\">\n <div className=\"text-4xl font-bold tabular-nums\">{provider.rating.toFixed(1)}</div>\n <div className=\"flex justify-center mt-1\">\n {[1, 2, 3, 4, 5].map((s) => (\n <Star\n key={s}\n className={`h-4 w-4 ${s <= Math.round(provider.rating) ? 'text-status-warning-text fill-current' : 'text-text-secondary'}`}\n />\n ))}\n </div>\n <p className=\"text-sm text-text-secondary mt-1\">\n {provider.totalReviews} reviews\n </p>\n </div>\n <div className=\"flex-1 space-y-1\">\n {[5, 4, 3, 2, 1].map((r) => {\n const c = providerReviews.filter((rv) => Math.round(rv.rating) === r).length;\n const p = providerReviews.length > 0 ? (c / providerReviews.length) * 100 : 0;\n return (\n <div key={r} className=\"flex items-center gap-2\">\n <span className=\"text-sm w-3 tabular-nums\">{r}</span>\n <Star className=\"h-3 w-3 text-status-warning-text fill-current\" />\n <div className=\"flex-1 bg-bg-sunken rounded-full h-2\">\n <div\n className=\"bg-status-warning-text h-2 rounded-full\"\n style={{ width: `${p}%` }}\n />\n </div>\n <span className=\"text-sm text-text-secondary w-6 text-right tabular-nums\">\n {c}\n </span>\n </div>\n );\n })}\n </div>\n </div>\n </CardContent>\n </GlassCard>\n {providerReviews.map((rev) => (\n <Card key={rev.id} className=\"border-border-seam\">\n <CardContent className=\"p-6\">\n <div className=\"flex items-center gap-3 mb-3\">\n <div className=\"h-10 w-10 rounded-full bg-accent-soft flex items-center justify-center\">\n <span className=\"text-text-primary font-medium\">\n {rev.clientName.charAt(0)}\n </span>\n </div>\n <div>\n <div className=\"flex items-center gap-2\">\n <span className=\"font-medium\">{rev.clientName}</span>\n <div className=\"flex\">\n {[1, 2, 3, 4, 5].map((s) => (\n <Star\n key={s}\n className={`h-3 w-3 ${s <= rev.rating ? 'text-status-warning-text fill-current' : 'text-text-secondary'}`}\n />\n ))}\n </div>\n </div>\n <p className=\"text-xs text-text-secondary\">\n {rev.serviceName} -{' '}\n {new Date(rev.createdAt).toLocaleDateString('en-US', {\n month: 'long',\n day: 'numeric',\n year: 'numeric',\n })}\n </p>\n </div>\n </div>\n <p className=\"text-sm text-text-secondary mb-3\">{rev.comment}</p>\n <div className=\"flex flex-wrap gap-4 text-xs text-text-secondary\">\n <span className=\"tabular-nums\">Value: ${rev.projectValue.toLocaleString()}</span>\n <span className=\"tabular-nums\">Duration: {rev.projectDuration} days</span>\n <span className=\"flex items-center\">\n <Users className=\"h-3 w-3 mr-1\" />\n <span className=\"tabular-nums\">{rev.helpful}</span> helpful\n </span>\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ProviderDetailPage.js","names":[],"sources":["../../../src/proserviceworld/pages/ProviderDetailPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport {\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Button,\n Badge,\n GlassCard,\n EmphasisPanel,\n CTAOverflowMenu,\n IllustratedEmptyState,\n PagePurpose,\n NextSteps,\n} from '@burdenoff/fe-libs/ui';\nimport {\n Star,\n CheckCircle,\n MapPin,\n Briefcase,\n Award,\n MessageSquare,\n Share2,\n Calendar,\n Users,\n} from 'lucide-react';\nimport { useProServiceWorldStore } from '../store/proserviceworldStore';\n\ntype DetailTab = 'overview' | 'services' | 'reviews';\n\nexport function ProviderDetailPage() {\n const { providers, services, reviews } = useProServiceWorldStore();\n const [activeTab, setActiveTab] = useState<DetailTab>('overview');\n\n const provider = providers[0];\n const providerServices = services.filter((s) => s.providerId === provider.id);\n const providerReviews = reviews.filter((r) => r.providerName === provider.name);\n\n const tabs: { id: DetailTab; label: string }[] = [\n { id: 'overview', label: 'Overview' },\n { id: 'services', label: `Services (${providerServices.length})` },\n { id: 'reviews', label: `Reviews (${providerReviews.length})` },\n ];\n\n return (\n <div className=\"space-y-6 p-6 bg-bg-canvas min-h-screen\">\n <PagePurpose icon={<Briefcase className=\"h-4 w-4\" />}>\n A full picture of this provider before you engage. Review their background, specializations,\n certifications, and ratings, browse the services they offer, and read client reviews — then\n contact them or book a consultation with confidence.\n </PagePurpose>\n\n <NextSteps\n storageKey=\"proserviceworld-provider-detail\"\n steps={[\n {\n id: 'services',\n label: 'Browse their services',\n description: 'See what this provider offers and starting prices.',\n icon: <Briefcase className=\"h-4 w-4\" />,\n onClick: () => setActiveTab('services'),\n },\n {\n id: 'reviews',\n label: 'Read client reviews',\n description: 'Check ratings and feedback from past clients.',\n icon: <Star className=\"h-4 w-4\" />,\n onClick: () => setActiveTab('reviews'),\n },\n {\n id: 'contact',\n label: 'Contact the provider',\n description: 'Start a conversation or book a consultation.',\n icon: <MessageSquare className=\"h-4 w-4\" />,\n onClick: () => setActiveTab('overview'),\n },\n ]}\n />\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2\">\n {/* Provider hero — the single emphasised zone for this page */}\n <EmphasisPanel className=\"relative overflow-hidden\">\n <div className=\"flex items-start space-x-6\">\n <div className=\"h-20 w-20 rounded-full bg-accent-soft flex items-center justify-center shrink-0\">\n <span className=\"text-text-primary text-2xl font-bold\">\n {provider.name.charAt(0)}\n {provider.name.split(' ')[1]?.charAt(0)}\n </span>\n </div>\n <div className=\"flex-1\">\n <div className=\"flex items-center gap-3 mb-1\">\n <h1 className=\"text-2xl font-bold tracking-tight\">{provider.businessName}</h1>\n {provider.verified && (\n <Badge variant=\"default\">\n <CheckCircle className=\"h-3 w-3 mr-1\" />\n Verified\n </Badge>\n )}\n </div>\n <p className=\"text-text-secondary mb-2\">{provider.name}</p>\n <p className=\"text-sm text-text-secondary mb-4\">{provider.tagline}</p>\n <div className=\"flex flex-wrap items-center gap-4 text-sm\">\n <div className=\"flex items-center\">\n <Star className=\"h-4 w-4 text-status-warning-text fill-current mr-1\" />\n <span className=\"font-medium tabular-nums\">{provider.rating.toFixed(1)}</span>\n <span className=\"text-text-secondary ml-1\">\n ({provider.totalReviews} reviews)\n </span>\n </div>\n <div className=\"flex items-center text-text-secondary\">\n <MapPin className=\"h-4 w-4 mr-1\" />\n {provider.location}\n </div>\n <div className=\"flex items-center text-text-secondary\">\n <Briefcase className=\"h-4 w-4 mr-1\" />\n {provider.yearsExperience} years\n </div>\n </div>\n </div>\n </div>\n </EmphasisPanel>\n </div>\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam\">\n <CardContent className=\"p-6 space-y-4\">\n <div className=\"text-center\">\n <div className=\"text-2xl font-bold tabular-nums\">${provider.hourlyRate}/hr</div>\n <p className=\"text-sm text-text-secondary\">Starting rate</p>\n </div>\n <Button className=\"w-full\">\n <MessageSquare className=\"h-4 w-4 mr-2\" />\n Contact Provider\n </Button>\n <CTAOverflowMenu\n className=\"justify-center\"\n actions={[\n {\n label: 'Book Consultation',\n onSelect: () => {},\n icon: <Calendar className=\"h-4 w-4\" />,\n },\n {\n label: 'Share Profile',\n onSelect: () => {},\n icon: <Share2 className=\"h-4 w-4\" />,\n },\n ]}\n />\n <div className=\"grid grid-cols-2 gap-4 text-sm pt-4 border-t border-border-seam\">\n <div className=\"text-center p-3 bg-bg-sunken rounded\">\n <div className=\"font-semibold tabular-nums\">{provider.responseTime}h</div>\n <div className=\"text-text-secondary\">Response</div>\n </div>\n <div className=\"text-center p-3 bg-bg-sunken rounded\">\n <div className=\"font-semibold tabular-nums\">{provider.completionRate}%</div>\n <div className=\"text-text-secondary\">Completion</div>\n </div>\n <div className=\"text-center p-3 bg-bg-sunken rounded\">\n <div className=\"font-semibold tabular-nums\">{provider.projectsCompleted}</div>\n <div className=\"text-text-secondary\">Projects</div>\n </div>\n <div className=\"text-center p-3 bg-bg-sunken rounded\">\n <div className=\"font-semibold tabular-nums\">{provider.activeProjects}</div>\n <div className=\"text-text-secondary\">Active</div>\n </div>\n </div>\n </CardContent>\n </GlassCard>\n </div>\n\n <div className=\"border-b border-border-seam\">\n <nav className=\"-mb-px flex space-x-8\">\n {tabs.map((tab) => (\n <button\n key={tab.id}\n onClick={() => setActiveTab(tab.id)}\n aria-pressed={activeTab === tab.id}\n className={`py-3 px-1 border-b-2 font-medium text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus ${activeTab === tab.id ? 'border-accent-blue text-accent-blue' : 'border-transparent text-text-secondary hover:text-text-primary'}`}\n >\n {tab.label}\n </button>\n ))}\n </nav>\n </div>\n\n {activeTab === 'overview' && (\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-6\">\n <Card className=\"border-border-seam\">\n <CardHeader>\n <CardTitle>About</CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-text-secondary\">{provider.description}</p>\n </CardContent>\n </Card>\n <Card className=\"border-border-seam\">\n <CardHeader>\n <CardTitle>Specializations</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"flex flex-wrap gap-2\">\n {provider.specializations.map((s) => (\n <Badge key={s} variant=\"secondary\">\n {s}\n </Badge>\n ))}\n </div>\n </CardContent>\n </Card>\n <Card className=\"border-border-seam\">\n <CardHeader>\n <CardTitle>Certifications</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {provider.certifications.map((c) => (\n <div key={c} className=\"flex items-center gap-3\">\n <Award className=\"h-5 w-5 text-accent-blue\" />\n <span>{c}</span>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n <div className=\"space-y-6\">\n <Card className=\"border-border-seam\">\n <CardHeader>\n <CardTitle>Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3 text-sm\">\n <div className=\"flex justify-between\">\n <span className=\"text-text-secondary\">Location</span>\n <span>{provider.location}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-text-secondary\">Languages</span>\n <span>{provider.languages.join(', ')}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-text-secondary\">Member Since</span>\n <span>\n {new Date(provider.joinedDate).toLocaleDateString('en-US', {\n month: 'short',\n year: 'numeric',\n })}\n </span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-text-secondary\">Category</span>\n <span className=\"capitalize\">{provider.category.replace('_', ' ')}</span>\n </div>\n </CardContent>\n </Card>\n <Card className=\"border-border-seam\">\n <CardHeader>\n <CardTitle>Similar Providers</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {providers\n .filter((p) => p.id !== provider.id)\n .slice(0, 3)\n .map((p) => (\n <div key={p.id} className=\"flex items-center gap-3\">\n <div className=\"h-8 w-8 rounded-full bg-bg-sunken flex items-center justify-center text-xs font-medium\">\n {p.name.charAt(0)}\n </div>\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium truncate\">{p.businessName}</p>\n <p className=\"text-xs text-text-secondary\">{p.specializations[0]}</p>\n </div>\n <div className=\"flex items-center text-xs\">\n <Star className=\"h-3 w-3 text-status-warning-text fill-current mr-0.5\" />\n <span className=\"tabular-nums\">{p.rating.toFixed(1)}</span>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n </div>\n )}\n\n {activeTab === 'services' && (\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6\">\n {providerServices.length > 0 ? (\n providerServices.map((svc) => (\n <GlassCard\n key={svc.id}\n treatment=\"plain\"\n className=\"border-border-seam transition-shadow hover:shadow-pop\"\n >\n <CardContent className=\"p-6\">\n <h3 className=\"font-semibold mb-2\">{svc.title}</h3>\n <p className=\"text-sm text-text-secondary mb-3 line-clamp-2\">\n {svc.description}\n </p>\n <div className=\"flex flex-wrap gap-1 mb-3\">\n {svc.tags.slice(0, 3).map((t) => (\n <Badge key={t} variant=\"outline\" className=\"text-xs\">\n {t}\n </Badge>\n ))}\n </div>\n <div className=\"flex justify-between border-t border-border-seam pt-3\">\n <span className=\"font-medium tabular-nums\">\n From ${svc.pricing.basePrice.toLocaleString()}\n </span>\n <span className=\"text-sm text-text-secondary tabular-nums\">\n {svc.duration.typical} days\n </span>\n </div>\n <Button variant=\"outline\" size=\"sm\" className=\"w-full mt-3\">\n View Details\n </Button>\n </CardContent>\n </GlassCard>\n ))\n ) : (\n <div className=\"col-span-full\">\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No services listed yet\"\n description=\"This provider has not published any services.\"\n />\n </div>\n )}\n </div>\n )}\n\n {activeTab === 'reviews' && (\n <div className=\"space-y-4\">\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardContent className=\"p-6\">\n <div className=\"flex flex-col gap-8 sm:flex-row sm:items-center\">\n <div className=\"text-center\">\n <div className=\"text-4xl font-bold tabular-nums\">{provider.rating.toFixed(1)}</div>\n <div className=\"flex justify-center mt-1\">\n {[1, 2, 3, 4, 5].map((s) => (\n <Star\n key={s}\n className={`h-4 w-4 ${s <= Math.round(provider.rating) ? 'text-status-warning-text fill-current' : 'text-text-secondary'}`}\n />\n ))}\n </div>\n <p className=\"text-sm text-text-secondary mt-1\">\n {provider.totalReviews} reviews\n </p>\n </div>\n <div className=\"flex-1 space-y-1\">\n {[5, 4, 3, 2, 1].map((r) => {\n const c = providerReviews.filter((rv) => Math.round(rv.rating) === r).length;\n const p = providerReviews.length > 0 ? (c / providerReviews.length) * 100 : 0;\n return (\n <div key={r} className=\"flex items-center gap-2\">\n <span className=\"text-sm w-3 tabular-nums\">{r}</span>\n <Star className=\"h-3 w-3 text-status-warning-text fill-current\" />\n <div className=\"flex-1 bg-bg-sunken rounded-full h-2\">\n <div\n className=\"bg-status-warning-text h-2 rounded-full\"\n style={{ width: `${p}%` }}\n />\n </div>\n <span className=\"text-sm text-text-secondary w-6 text-right tabular-nums\">\n {c}\n </span>\n </div>\n );\n })}\n </div>\n </div>\n </CardContent>\n </GlassCard>\n {providerReviews.map((rev) => (\n <Card key={rev.id} className=\"border-border-seam\">\n <CardContent className=\"p-6\">\n <div className=\"flex items-center gap-3 mb-3\">\n <div className=\"h-10 w-10 rounded-full bg-accent-soft flex items-center justify-center\">\n <span className=\"text-text-primary font-medium\">\n {rev.clientName.charAt(0)}\n </span>\n </div>\n <div>\n <div className=\"flex items-center gap-2\">\n <span className=\"font-medium\">{rev.clientName}</span>\n <div className=\"flex\">\n {[1, 2, 3, 4, 5].map((s) => (\n <Star\n key={s}\n className={`h-3 w-3 ${s <= rev.rating ? 'text-status-warning-text fill-current' : 'text-text-secondary'}`}\n />\n ))}\n </div>\n </div>\n <p className=\"text-xs text-text-secondary\">\n {rev.serviceName} -{' '}\n {new Date(rev.createdAt).toLocaleDateString('en-US', {\n month: 'long',\n day: 'numeric',\n year: 'numeric',\n })}\n </p>\n </div>\n </div>\n <p className=\"text-sm text-text-secondary mb-3\">{rev.comment}</p>\n <div className=\"flex flex-wrap gap-4 text-xs text-text-secondary\">\n <span className=\"tabular-nums\">Value: ${rev.projectValue.toLocaleString()}</span>\n <span className=\"tabular-nums\">Duration: {rev.projectDuration} days</span>\n <span className=\"flex items-center\">\n <Users className=\"h-3 w-3 mr-1\" />\n <span className=\"tabular-nums\">{rev.helpful}</span> helpful\n </span>\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -4,9 +4,9 @@ import { useMemo as n, useState as r } from "react";
|
|
|
4
4
|
import { Briefcase as i, CheckCircle as a, Clock as o, Filter as s, MapPin as c, Search as l, Star as u, Users as d } from "lucide-react";
|
|
5
5
|
import { useI18n as f } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
6
6
|
import { jsx as p, jsxs as m } from "react/jsx-runtime";
|
|
7
|
-
import { AuroraBackground as h, Badge as g, Button as _, Card as v, CardContent as y, EmphasisPanel as b, GlassCard as x, GradientText as S, IllustratedEmptyState as C, Input as w } from "@burdenoff/fe-libs/ui";
|
|
7
|
+
import { AuroraBackground as h, Badge as g, Button as _, Card as v, CardContent as y, EmphasisPanel as b, GlassCard as x, GradientText as S, IllustratedEmptyState as C, Input as w, PagePurpose as T } from "@burdenoff/fe-libs/ui";
|
|
8
8
|
//#region src/proserviceworld/pages/ProvidersPage.tsx
|
|
9
|
-
var
|
|
9
|
+
var E = [
|
|
10
10
|
{
|
|
11
11
|
value: "",
|
|
12
12
|
key: "proserviceworld.shared.filters.allCategories",
|
|
@@ -58,25 +58,25 @@ var T = [
|
|
|
58
58
|
fallback: "Education & Training"
|
|
59
59
|
}
|
|
60
60
|
];
|
|
61
|
-
function
|
|
62
|
-
let { t:
|
|
63
|
-
let e = [...
|
|
64
|
-
if (
|
|
65
|
-
let t =
|
|
61
|
+
function D() {
|
|
62
|
+
let { t: D } = f(), O = (e, n, r) => t(D, e, n, r), { providers: k } = e(), [A, j] = r(""), [M, N] = r(""), [P, F] = r(""), [I, L] = r(!1), R = n(() => {
|
|
63
|
+
let e = [...k];
|
|
64
|
+
if (A) {
|
|
65
|
+
let t = A.toLowerCase();
|
|
66
66
|
e = e.filter((e) => e.name.toLowerCase().includes(t) || e.businessName.toLowerCase().includes(t) || e.tagline.toLowerCase().includes(t) || e.specializations.some((e) => e.toLowerCase().includes(t)));
|
|
67
67
|
}
|
|
68
|
-
return
|
|
68
|
+
return M && (e = e.filter((e) => e.category === M)), P && (e = e.filter((e) => e.rating >= P)), e.sort((e, t) => t.rating - e.rating), e;
|
|
69
69
|
}, [
|
|
70
|
-
O,
|
|
71
70
|
k,
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
71
|
+
A,
|
|
72
|
+
M,
|
|
73
|
+
P
|
|
74
|
+
]), z = R.length > 0 ? (R.reduce((e, t) => e + t.rating, 0) / R.length).toFixed(1) : "0", B = new Set(R.map((e) => e.country)).size, V = () => {
|
|
75
|
+
j(""), N(""), F("");
|
|
76
|
+
}, H = A ? O("proserviceworld.providers.subtitleWithQuery", "{{count}} providers found for \"{{query}}\"", {
|
|
77
|
+
count: R.length,
|
|
78
|
+
query: A
|
|
79
|
+
}) : O("proserviceworld.providers.subtitle", "{{count}} providers found", { count: R.length });
|
|
80
80
|
return /* @__PURE__ */ m("div", {
|
|
81
81
|
className: "space-y-6 p-6 bg-bg-canvas min-h-screen",
|
|
82
82
|
children: [
|
|
@@ -86,32 +86,36 @@ function E() {
|
|
|
86
86
|
className: "flex flex-col md:flex-row md:items-center md:justify-between gap-4",
|
|
87
87
|
children: [/* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("h1", {
|
|
88
88
|
className: "text-2xl font-semibold tracking-tight",
|
|
89
|
-
children: /* @__PURE__ */ p(S, { children:
|
|
89
|
+
children: /* @__PURE__ */ p(S, { children: O("proserviceworld.providers.title", "Service Providers") })
|
|
90
90
|
}), /* @__PURE__ */ p("p", {
|
|
91
91
|
className: "text-sm text-text-secondary",
|
|
92
|
-
children:
|
|
92
|
+
children: H
|
|
93
93
|
})] }), /* @__PURE__ */ m(_, {
|
|
94
94
|
variant: "outline",
|
|
95
|
-
onClick: () =>
|
|
96
|
-
"aria-expanded":
|
|
97
|
-
children: [/* @__PURE__ */ p(s, { className: "h-4 w-4 mr-2" }),
|
|
95
|
+
onClick: () => L(!I),
|
|
96
|
+
"aria-expanded": I,
|
|
97
|
+
children: [/* @__PURE__ */ p(s, { className: "h-4 w-4 mr-2" }), O("proserviceworld.providers.actions.filters", "Filters")]
|
|
98
98
|
})]
|
|
99
99
|
})]
|
|
100
100
|
}),
|
|
101
|
+
/* @__PURE__ */ p(T, {
|
|
102
|
+
icon: /* @__PURE__ */ p(d, { className: "h-4 w-4" }),
|
|
103
|
+
children: O("proserviceworld.providers.purpose", "Find and vet the experts behind the work. Search by name, business, or specialization and filter by category and rating to shortlist trusted providers — then open a profile to view their services, reviews, and credentials before reaching out.")
|
|
104
|
+
}),
|
|
101
105
|
/* @__PURE__ */ p("div", {
|
|
102
106
|
className: "flex gap-4",
|
|
103
107
|
children: /* @__PURE__ */ m("div", {
|
|
104
108
|
className: "flex-1 relative",
|
|
105
109
|
children: [/* @__PURE__ */ p(l, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-text-secondary" }), /* @__PURE__ */ p(w, {
|
|
106
110
|
type: "text",
|
|
107
|
-
placeholder:
|
|
108
|
-
value:
|
|
109
|
-
onChange: (e) =>
|
|
111
|
+
placeholder: O("proserviceworld.providers.search.placeholder", "Search providers by name, business, or specialization..."),
|
|
112
|
+
value: A,
|
|
113
|
+
onChange: (e) => j(e.target.value),
|
|
110
114
|
className: "pl-10 h-12"
|
|
111
115
|
})]
|
|
112
116
|
})
|
|
113
117
|
}),
|
|
114
|
-
|
|
118
|
+
I && /* @__PURE__ */ p(v, {
|
|
115
119
|
className: "border-border-seam",
|
|
116
120
|
children: /* @__PURE__ */ p(y, {
|
|
117
121
|
className: "p-6",
|
|
@@ -120,39 +124,39 @@ function E() {
|
|
|
120
124
|
children: [
|
|
121
125
|
/* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
|
|
122
126
|
className: "block text-sm font-medium mb-2",
|
|
123
|
-
children:
|
|
127
|
+
children: O("proserviceworld.providers.filters.category", "Category")
|
|
124
128
|
}), /* @__PURE__ */ p("select", {
|
|
125
|
-
value:
|
|
126
|
-
onChange: (e) =>
|
|
129
|
+
value: M,
|
|
130
|
+
onChange: (e) => N(e.target.value),
|
|
127
131
|
className: "w-full p-2 border border-border-seam rounded-md bg-bg-surface text-text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus",
|
|
128
|
-
children:
|
|
132
|
+
children: E.map((e) => /* @__PURE__ */ p("option", {
|
|
129
133
|
value: e.value,
|
|
130
|
-
children:
|
|
134
|
+
children: O(e.key, e.fallback)
|
|
131
135
|
}, e.value))
|
|
132
136
|
})] }),
|
|
133
137
|
/* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
|
|
134
138
|
className: "block text-sm font-medium mb-2",
|
|
135
|
-
children:
|
|
139
|
+
children: O("proserviceworld.providers.filters.minimumRating", "Minimum Rating")
|
|
136
140
|
}), /* @__PURE__ */ m("select", {
|
|
137
|
-
value:
|
|
138
|
-
onChange: (e) =>
|
|
141
|
+
value: P,
|
|
142
|
+
onChange: (e) => F(e.target.value ? Number(e.target.value) : ""),
|
|
139
143
|
className: "w-full p-2 border border-border-seam rounded-md bg-bg-surface text-text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus",
|
|
140
144
|
children: [
|
|
141
145
|
/* @__PURE__ */ p("option", {
|
|
142
146
|
value: "",
|
|
143
|
-
children:
|
|
147
|
+
children: O("proserviceworld.providers.filters.anyRating", "Any Rating")
|
|
144
148
|
}),
|
|
145
149
|
/* @__PURE__ */ p("option", {
|
|
146
150
|
value: "4",
|
|
147
|
-
children:
|
|
151
|
+
children: O("proserviceworld.providers.filters.rating4", "4+ Stars")
|
|
148
152
|
}),
|
|
149
153
|
/* @__PURE__ */ p("option", {
|
|
150
154
|
value: "4.5",
|
|
151
|
-
children:
|
|
155
|
+
children: O("proserviceworld.providers.filters.rating45", "4.5+ Stars")
|
|
152
156
|
}),
|
|
153
157
|
/* @__PURE__ */ p("option", {
|
|
154
158
|
value: "4.8",
|
|
155
|
-
children:
|
|
159
|
+
children: O("proserviceworld.providers.filters.rating48", "4.8+ Stars")
|
|
156
160
|
})
|
|
157
161
|
]
|
|
158
162
|
})] }),
|
|
@@ -160,18 +164,18 @@ function E() {
|
|
|
160
164
|
className: "flex items-end",
|
|
161
165
|
children: /* @__PURE__ */ p(_, {
|
|
162
166
|
variant: "outline",
|
|
163
|
-
onClick:
|
|
167
|
+
onClick: V,
|
|
164
168
|
className: "w-full",
|
|
165
|
-
children:
|
|
169
|
+
children: O("proserviceworld.providers.filters.clear", "Clear Filters")
|
|
166
170
|
})
|
|
167
171
|
})
|
|
168
172
|
]
|
|
169
173
|
})
|
|
170
174
|
})
|
|
171
175
|
}),
|
|
172
|
-
|
|
176
|
+
R.length > 0 ? /* @__PURE__ */ p("div", {
|
|
173
177
|
className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6",
|
|
174
|
-
children:
|
|
178
|
+
children: R.map((e) => /* @__PURE__ */ m(x, {
|
|
175
179
|
treatment: "glass",
|
|
176
180
|
className: "border-border-seam transition-shadow hover:shadow-pop",
|
|
177
181
|
children: [
|
|
@@ -245,37 +249,37 @@ function E() {
|
|
|
245
249
|
className: "grid grid-cols-2 gap-2 text-xs text-text-secondary border-t border-border-seam pt-3",
|
|
246
250
|
children: [/* @__PURE__ */ m("div", {
|
|
247
251
|
className: "flex items-center",
|
|
248
|
-
children: [/* @__PURE__ */ p(o, { className: "h-3 w-3 mr-1" }),
|
|
252
|
+
children: [/* @__PURE__ */ p(o, { className: "h-3 w-3 mr-1" }), O("proserviceworld.providers.card.responseTime", "{{count}}h response", { count: e.responseTime })]
|
|
249
253
|
}), /* @__PURE__ */ m("div", {
|
|
250
254
|
className: "flex items-center",
|
|
251
|
-
children: [/* @__PURE__ */ p(i, { className: "h-3 w-3 mr-1" }),
|
|
255
|
+
children: [/* @__PURE__ */ p(i, { className: "h-3 w-3 mr-1" }), O("proserviceworld.providers.card.projects", "{{count}} projects", { count: e.projectsCompleted })]
|
|
252
256
|
})]
|
|
253
257
|
}),
|
|
254
258
|
/* @__PURE__ */ p(_, {
|
|
255
259
|
variant: "outline",
|
|
256
260
|
size: "sm",
|
|
257
261
|
className: "w-full mt-4",
|
|
258
|
-
children:
|
|
262
|
+
children: O("proserviceworld.providers.actions.viewProfile", "View Profile")
|
|
259
263
|
})
|
|
260
264
|
]
|
|
261
265
|
}, e.id))
|
|
262
266
|
}) : /* @__PURE__ */ p(C, {
|
|
263
267
|
illustration: "empty-people",
|
|
264
|
-
title:
|
|
265
|
-
description:
|
|
268
|
+
title: O("proserviceworld.providers.empty.title", "No providers found"),
|
|
269
|
+
description: O("proserviceworld.shared.empty.adjustFilters", "Try adjusting your search criteria or filters."),
|
|
266
270
|
action: /* @__PURE__ */ p(_, {
|
|
267
271
|
variant: "outline",
|
|
268
|
-
onClick:
|
|
269
|
-
children:
|
|
272
|
+
onClick: V,
|
|
273
|
+
children: O("proserviceworld.providers.filters.clear", "Clear Filters")
|
|
270
274
|
})
|
|
271
275
|
}),
|
|
272
|
-
|
|
276
|
+
R.length > 0 && /* @__PURE__ */ m(b, {
|
|
273
277
|
className: "p-8",
|
|
274
278
|
children: [/* @__PURE__ */ p("div", {
|
|
275
279
|
className: "text-center mb-8",
|
|
276
280
|
children: /* @__PURE__ */ p("h2", {
|
|
277
281
|
className: "text-xl font-bold tracking-tight",
|
|
278
|
-
children:
|
|
282
|
+
children: O("proserviceworld.providers.stats.title", "Provider Statistics")
|
|
279
283
|
})
|
|
280
284
|
}), /* @__PURE__ */ m("div", {
|
|
281
285
|
className: "grid grid-cols-1 md:grid-cols-3 gap-8",
|
|
@@ -289,11 +293,11 @@ function E() {
|
|
|
289
293
|
}),
|
|
290
294
|
/* @__PURE__ */ p("div", {
|
|
291
295
|
className: "text-2xl font-bold tabular-nums",
|
|
292
|
-
children:
|
|
296
|
+
children: z
|
|
293
297
|
}),
|
|
294
298
|
/* @__PURE__ */ p("div", {
|
|
295
299
|
className: "text-sm text-text-secondary",
|
|
296
|
-
children:
|
|
300
|
+
children: O("proserviceworld.providers.stats.averageRating", "Average Rating")
|
|
297
301
|
})
|
|
298
302
|
]
|
|
299
303
|
}),
|
|
@@ -306,11 +310,11 @@ function E() {
|
|
|
306
310
|
}),
|
|
307
311
|
/* @__PURE__ */ p("div", {
|
|
308
312
|
className: "text-2xl font-bold tabular-nums",
|
|
309
|
-
children:
|
|
313
|
+
children: R.length
|
|
310
314
|
}),
|
|
311
315
|
/* @__PURE__ */ p("div", {
|
|
312
316
|
className: "text-sm text-text-secondary",
|
|
313
|
-
children:
|
|
317
|
+
children: O("proserviceworld.providers.stats.expertProviders", "Expert Providers")
|
|
314
318
|
})
|
|
315
319
|
]
|
|
316
320
|
}),
|
|
@@ -323,11 +327,11 @@ function E() {
|
|
|
323
327
|
}),
|
|
324
328
|
/* @__PURE__ */ p("div", {
|
|
325
329
|
className: "text-2xl font-bold tabular-nums",
|
|
326
|
-
children:
|
|
330
|
+
children: B
|
|
327
331
|
}),
|
|
328
332
|
/* @__PURE__ */ p("div", {
|
|
329
333
|
className: "text-sm text-text-secondary",
|
|
330
|
-
children:
|
|
334
|
+
children: O("proserviceworld.providers.stats.countries", "Countries")
|
|
331
335
|
})
|
|
332
336
|
]
|
|
333
337
|
})
|
|
@@ -338,6 +342,6 @@ function E() {
|
|
|
338
342
|
});
|
|
339
343
|
}
|
|
340
344
|
//#endregion
|
|
341
|
-
export {
|
|
345
|
+
export { D as ProvidersPage };
|
|
342
346
|
|
|
343
347
|
//# sourceMappingURL=ProvidersPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProvidersPage.js","names":[],"sources":["../../../src/proserviceworld/pages/ProvidersPage.tsx"],"sourcesContent":["import { useState, useMemo } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell';\nimport {\n Card,\n CardContent,\n Button,\n Badge,\n Input,\n GlassCard,\n EmphasisPanel,\n AuroraBackground,\n GradientText,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\nimport { Search, Filter, Star, Users, MapPin, CheckCircle, Clock, Briefcase } from 'lucide-react';\nimport { useProServiceWorldStore } from '../store/proserviceworldStore';\nimport type { ServiceCategory } from '../types';\nimport { tWithFallback } from '../utils/i18n';\n\nconst categoryOptions: { value: ServiceCategory | ''; key: string; fallback: string }[] = [\n {\n value: '',\n key: 'proserviceworld.shared.filters.allCategories',\n fallback: 'All Categories',\n },\n {\n value: 'business_consulting',\n key: 'proserviceworld.shared.categories.businessConsulting',\n fallback: 'Business Consulting',\n },\n {\n value: 'financial_services',\n key: 'proserviceworld.shared.categories.financialServices',\n fallback: 'Financial Services',\n },\n {\n value: 'legal_services',\n key: 'proserviceworld.shared.categories.legalServices',\n fallback: 'Legal Services',\n },\n {\n value: 'accounting_bookkeeping',\n key: 'proserviceworld.shared.categories.accountingBookkeeping',\n fallback: 'Accounting & Bookkeeping',\n },\n {\n value: 'marketing_advertising',\n key: 'proserviceworld.shared.categories.marketingAdvertising',\n fallback: 'Marketing & Advertising',\n },\n {\n value: 'it_technology',\n key: 'proserviceworld.shared.categories.itTechnology',\n fallback: 'IT & Technology',\n },\n {\n value: 'human_resources',\n key: 'proserviceworld.shared.categories.humanResources',\n fallback: 'Human Resources',\n },\n {\n value: 'creative_design',\n key: 'proserviceworld.shared.categories.creativeDesign',\n fallback: 'Creative & Design',\n },\n {\n value: 'education_training',\n key: 'proserviceworld.shared.categories.educationTraining',\n fallback: 'Education & Training',\n },\n];\n\nexport function ProvidersPage() {\n const { t } = useI18n();\n const tr = (key: string, fallback: string, params?: Record<string, string | number>): string =>\n tWithFallback(t, key, fallback, params);\n const { providers } = useProServiceWorldStore();\n const [searchQuery, setSearchQuery] = useState('');\n const [selectedCategory, setSelectedCategory] = useState<ServiceCategory | ''>('');\n const [minRating, setMinRating] = useState<number | ''>('');\n const [showFilters, setShowFilters] = useState(false);\n\n const filteredProviders = useMemo(() => {\n let result = [...providers];\n\n if (searchQuery) {\n const q = searchQuery.toLowerCase();\n result = result.filter(\n (p) =>\n p.name.toLowerCase().includes(q) ||\n p.businessName.toLowerCase().includes(q) ||\n p.tagline.toLowerCase().includes(q) ||\n p.specializations.some((s) => s.toLowerCase().includes(q))\n );\n }\n\n if (selectedCategory) {\n result = result.filter((p) => p.category === selectedCategory);\n }\n\n if (minRating) {\n result = result.filter((p) => p.rating >= minRating);\n }\n\n result.sort((a, b) => b.rating - a.rating);\n return result;\n }, [providers, searchQuery, selectedCategory, minRating]);\n\n const avgRating =\n filteredProviders.length > 0\n ? (\n filteredProviders.reduce((sum, p) => sum + p.rating, 0) / filteredProviders.length\n ).toFixed(1)\n : '0';\n\n const countries = new Set(filteredProviders.map((p) => p.country)).size;\n\n const clearFilters = () => {\n setSearchQuery('');\n setSelectedCategory('');\n setMinRating('');\n };\n const subtitle = searchQuery\n ? tr('proserviceworld.providers.subtitleWithQuery', '{{count}} providers found for \"{{query}}\"', {\n count: filteredProviders.length,\n query: searchQuery,\n })\n : tr('proserviceworld.providers.subtitle', '{{count}} providers found', {\n count: filteredProviders.length,\n });\n\n return (\n <div className=\"space-y-6 p-6 bg-bg-canvas min-h-screen\">\n {/* Hero header — the ONE aurora backdrop for this MFE */}\n <div className=\"relative overflow-hidden rounded-card -mx-2 px-2 pt-1\">\n <AuroraBackground intensity=\"subtle\" />\n <div className=\"flex flex-col md:flex-row md:items-center md:justify-between gap-4\">\n <div>\n <h1 className=\"text-2xl font-semibold tracking-tight\">\n <GradientText>{tr('proserviceworld.providers.title', 'Service Providers')}</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">{subtitle}</p>\n </div>\n <Button\n variant=\"outline\"\n onClick={() => setShowFilters(!showFilters)}\n aria-expanded={showFilters}\n >\n <Filter className=\"h-4 w-4 mr-2\" />\n {tr('proserviceworld.providers.actions.filters', 'Filters')}\n </Button>\n </div>\n </div>\n\n {/* Search Bar */}\n <div className=\"flex gap-4\">\n <div className=\"flex-1 relative\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-text-secondary\" />\n <Input\n type=\"text\"\n placeholder={tr(\n 'proserviceworld.providers.search.placeholder',\n 'Search providers by name, business, or specialization...'\n )}\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"pl-10 h-12\"\n />\n </div>\n </div>\n\n {/* Filters Panel */}\n {showFilters && (\n <Card className=\"border-border-seam\">\n <CardContent className=\"p-6\">\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-4\">\n <div>\n <label className=\"block text-sm font-medium mb-2\">\n {tr('proserviceworld.providers.filters.category', 'Category')}\n </label>\n <select\n value={selectedCategory}\n onChange={(e) => setSelectedCategory(e.target.value as ServiceCategory | '')}\n className=\"w-full p-2 border border-border-seam rounded-md bg-bg-surface text-text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n {categoryOptions.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {tr(opt.key, opt.fallback)}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"block text-sm font-medium mb-2\">\n {tr('proserviceworld.providers.filters.minimumRating', 'Minimum Rating')}\n </label>\n <select\n value={minRating}\n onChange={(e) => setMinRating(e.target.value ? Number(e.target.value) : '')}\n className=\"w-full p-2 border border-border-seam rounded-md bg-bg-surface text-text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n <option value=\"\">\n {tr('proserviceworld.providers.filters.anyRating', 'Any Rating')}\n </option>\n <option value=\"4\">\n {tr('proserviceworld.providers.filters.rating4', '4+ Stars')}\n </option>\n <option value=\"4.5\">\n {tr('proserviceworld.providers.filters.rating45', '4.5+ Stars')}\n </option>\n <option value=\"4.8\">\n {tr('proserviceworld.providers.filters.rating48', '4.8+ Stars')}\n </option>\n </select>\n </div>\n <div className=\"flex items-end\">\n <Button variant=\"outline\" onClick={clearFilters} className=\"w-full\">\n {tr('proserviceworld.providers.filters.clear', 'Clear Filters')}\n </Button>\n </div>\n </div>\n </CardContent>\n </Card>\n )}\n\n {/* Provider Grid */}\n {filteredProviders.length > 0 ? (\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6\">\n {filteredProviders.map((provider) => (\n <GlassCard key={provider.id} treatment=\"glass\" className=\"border-border-seam transition-shadow hover:shadow-pop\">\n <div className=\"flex items-center space-x-4 mb-4\">\n <div className=\"h-12 w-12 rounded-full bg-accent-soft flex items-center justify-center\">\n <span className=\"text-text-primary font-bold\">{provider.name.charAt(0)}</span>\n </div>\n <div className=\"flex-1 min-w-0\">\n <h3 className=\"font-semibold truncate\">{provider.businessName}</h3>\n <p className=\"text-sm text-text-secondary truncate\">{provider.name}</p>\n </div>\n {provider.verified && (\n <CheckCircle className=\"h-5 w-5 text-accent-blue shrink-0\" aria-label=\"Verified\" />\n )}\n </div>\n\n <p className=\"text-sm text-text-secondary mb-3 line-clamp-2\">{provider.tagline}</p>\n\n <div className=\"flex flex-wrap gap-1 mb-3\">\n {provider.specializations.slice(0, 2).map((spec) => (\n <Badge key={spec} variant=\"secondary\" className=\"text-xs\">\n {spec}\n </Badge>\n ))}\n {provider.specializations.length > 2 && (\n <Badge variant=\"outline\" className=\"text-xs\">\n +{provider.specializations.length - 2}\n </Badge>\n )}\n </div>\n\n <div className=\"flex items-center justify-between text-sm mb-3\">\n <div className=\"flex items-center\">\n <Star className=\"h-4 w-4 text-status-warning-text fill-current mr-1\" />\n <span className=\"font-medium tabular-nums\">{provider.rating.toFixed(1)}</span>\n <span className=\"text-text-secondary ml-1 tabular-nums\">\n ({provider.totalReviews})\n </span>\n </div>\n <div className=\"flex items-center text-text-secondary\">\n <MapPin className=\"h-3 w-3 mr-1\" />\n <span>{provider.location}</span>\n </div>\n </div>\n\n <div className=\"grid grid-cols-2 gap-2 text-xs text-text-secondary border-t border-border-seam pt-3\">\n <div className=\"flex items-center\">\n <Clock className=\"h-3 w-3 mr-1\" />\n {tr('proserviceworld.providers.card.responseTime', '{{count}}h response', {\n count: provider.responseTime,\n })}\n </div>\n <div className=\"flex items-center\">\n <Briefcase className=\"h-3 w-3 mr-1\" />\n {tr('proserviceworld.providers.card.projects', '{{count}} projects', {\n count: provider.projectsCompleted,\n })}\n </div>\n </div>\n\n <Button variant=\"outline\" size=\"sm\" className=\"w-full mt-4\">\n {tr('proserviceworld.providers.actions.viewProfile', 'View Profile')}\n </Button>\n </GlassCard>\n ))}\n </div>\n ) : (\n <IllustratedEmptyState\n illustration=\"empty-people\"\n title={tr('proserviceworld.providers.empty.title', 'No providers found')}\n description={tr(\n 'proserviceworld.shared.empty.adjustFilters',\n 'Try adjusting your search criteria or filters.'\n )}\n action={\n <Button variant=\"outline\" onClick={clearFilters}>\n {tr('proserviceworld.providers.filters.clear', 'Clear Filters')}\n </Button>\n }\n />\n )}\n\n {/* Provider Statistics — the single emphasised insight zone */}\n {filteredProviders.length > 0 && (\n <EmphasisPanel className=\"p-8\">\n <div className=\"text-center mb-8\">\n <h2 className=\"text-xl font-bold tracking-tight\">\n {tr('proserviceworld.providers.stats.title', 'Provider Statistics')}\n </h2>\n </div>\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-8\">\n <div className=\"text-center\">\n <div className=\"flex items-center justify-center w-12 h-12 mx-auto bg-accent-soft rounded-lg mb-4\">\n <Star className=\"h-6 w-6 text-text-primary\" />\n </div>\n <div className=\"text-2xl font-bold tabular-nums\">{avgRating}</div>\n <div className=\"text-sm text-text-secondary\">\n {tr('proserviceworld.providers.stats.averageRating', 'Average Rating')}\n </div>\n </div>\n <div className=\"text-center\">\n <div className=\"flex items-center justify-center w-12 h-12 mx-auto bg-accent-soft rounded-lg mb-4\">\n <Users className=\"h-6 w-6 text-text-primary\" />\n </div>\n <div className=\"text-2xl font-bold tabular-nums\">{filteredProviders.length}</div>\n <div className=\"text-sm text-text-secondary\">\n {tr('proserviceworld.providers.stats.expertProviders', 'Expert Providers')}\n </div>\n </div>\n <div className=\"text-center\">\n <div className=\"flex items-center justify-center w-12 h-12 mx-auto bg-accent-soft rounded-lg mb-4\">\n <MapPin className=\"h-6 w-6 text-text-primary\" />\n </div>\n <div className=\"text-2xl font-bold tabular-nums\">{countries}</div>\n <div className=\"text-sm text-text-secondary\">\n {tr('proserviceworld.providers.stats.countries', 'Countries')}\n </div>\n </div>\n </div>\n </EmphasisPanel>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;AAmBA,IAAM,IAAoF;CACxF;EACE,OAAO;EACP,KAAK;EACL,UAAU;EACX;CACD;EACE,OAAO;EACP,KAAK;EACL,UAAU;EACX;CACD;EACE,OAAO;EACP,KAAK;EACL,UAAU;EACX;CACD;EACE,OAAO;EACP,KAAK;EACL,UAAU;EACX;CACD;EACE,OAAO;EACP,KAAK;EACL,UAAU;EACX;CACD;EACE,OAAO;EACP,KAAK;EACL,UAAU;EACX;CACD;EACE,OAAO;EACP,KAAK;EACL,UAAU;EACX;CACD;EACE,OAAO;EACP,KAAK;EACL,UAAU;EACX;CACD;EACE,OAAO;EACP,KAAK;EACL,UAAU;EACX;CACD;EACE,OAAO;EACP,KAAK;EACL,UAAU;EACX;CACF;AAED,SAAgB,IAAgB;CAC9B,IAAM,EAAE,SAAM,GAAS,EACjB,KAAM,GAAa,GAAkB,MACzC,EAAc,GAAG,GAAK,GAAU,EAAO,EACnC,EAAE,iBAAc,GAAyB,EACzC,CAAC,GAAa,KAAkB,EAAS,GAAG,EAC5C,CAAC,GAAkB,KAAuB,EAA+B,GAAG,EAC5E,CAAC,GAAW,KAAgB,EAAsB,GAAG,EACrD,CAAC,GAAa,KAAkB,EAAS,GAAM,EAE/C,IAAoB,QAAc;EACtC,IAAI,IAAS,CAAC,GAAG,EAAU;AAE3B,MAAI,GAAa;GACf,IAAM,IAAI,EAAY,aAAa;AACnC,OAAS,EAAO,QACb,MACC,EAAE,KAAK,aAAa,CAAC,SAAS,EAAE,IAChC,EAAE,aAAa,aAAa,CAAC,SAAS,EAAE,IACxC,EAAE,QAAQ,aAAa,CAAC,SAAS,EAAE,IACnC,EAAE,gBAAgB,MAAM,MAAM,EAAE,aAAa,CAAC,SAAS,EAAE,CAAC,CAC7D;;AAYH,SATI,MACF,IAAS,EAAO,QAAQ,MAAM,EAAE,aAAa,EAAiB,GAG5D,MACF,IAAS,EAAO,QAAQ,MAAM,EAAE,UAAU,EAAU,GAGtD,EAAO,MAAM,GAAG,MAAM,EAAE,SAAS,EAAE,OAAO,EACnC;IACN;EAAC;EAAW;EAAa;EAAkB;EAAU,CAAC,EAEnD,IACJ,EAAkB,SAAS,KAErB,EAAkB,QAAQ,GAAK,MAAM,IAAM,EAAE,QAAQ,EAAE,GAAG,EAAkB,QAC5E,QAAQ,EAAE,GACZ,KAEA,IAAY,IAAI,IAAI,EAAkB,KAAK,MAAM,EAAE,QAAQ,CAAC,CAAC,MAE7D,UAAqB;AAGzB,EAFA,EAAe,GAAG,EAClB,EAAoB,GAAG,EACvB,EAAa,GAAG;IAEZ,IAAW,IACb,EAAG,+CAA+C,+CAA6C;EAC7F,OAAO,EAAkB;EACzB,OAAO;EACR,CAAC,GACF,EAAG,sCAAsC,6BAA6B,EACpE,OAAO,EAAkB,QAC1B,CAAC;AAEN,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GAEE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD,EAAkB,WAAU,UAAW,CAAA,EACvC,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;MAAI,WAAU;gBACZ,kBAAC,GAAD,EAAA,UAAe,EAAG,mCAAmC,oBAAoB,EAAgB,CAAA;MACtF,CAAA,EACL,kBAAC,KAAD;MAAG,WAAU;gBAA+B;MAAa,CAAA,CACrD,EAAA,CAAA,EACN,kBAAC,GAAD;MACE,SAAQ;MACR,eAAe,EAAe,CAAC,EAAY;MAC3C,iBAAe;gBAHjB,CAKE,kBAAC,GAAD,EAAQ,WAAU,gBAAiB,CAAA,EAClC,EAAG,6CAA6C,UAAU,CACpD;QACL;OACF;;GAGN,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAQ,WAAU,wEAAyE,CAAA,EAC3F,kBAAC,GAAD;MACE,MAAK;MACL,aAAa,EACX,gDACA,2DACD;MACD,OAAO;MACP,WAAW,MAAM,EAAe,EAAE,OAAO,MAAM;MAC/C,WAAU;MACV,CAAA,CACE;;IACF,CAAA;GAGL,KACC,kBAAC,GAAD;IAAM,WAAU;cACd,kBAAC,GAAD;KAAa,WAAU;eACrB,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;QAAO,WAAU;kBACd,EAAG,8CAA8C,WAAW;QACvD,CAAA,EACR,kBAAC,UAAD;QACE,OAAO;QACP,WAAW,MAAM,EAAoB,EAAE,OAAO,MAA8B;QAC5E,WAAU;kBAET,EAAgB,KAAK,MACpB,kBAAC,UAAD;SAAwB,OAAO,EAAI;mBAChC,EAAG,EAAI,KAAK,EAAI,SAAS;SACnB,EAFI,EAAI,MAER,CACT;QACK,CAAA,CACL,EAAA,CAAA;OACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;QAAO,WAAU;kBACd,EAAG,mDAAmD,iBAAiB;QAClE,CAAA,EACR,kBAAC,UAAD;QACE,OAAO;QACP,WAAW,MAAM,EAAa,EAAE,OAAO,QAAQ,OAAO,EAAE,OAAO,MAAM,GAAG,GAAG;QAC3E,WAAU;kBAHZ;SAKE,kBAAC,UAAD;UAAQ,OAAM;oBACX,EAAG,+CAA+C,aAAa;UACzD,CAAA;SACT,kBAAC,UAAD;UAAQ,OAAM;oBACX,EAAG,6CAA6C,WAAW;UACrD,CAAA;SACT,kBAAC,UAAD;UAAQ,OAAM;oBACX,EAAG,8CAA8C,aAAa;UACxD,CAAA;SACT,kBAAC,UAAD;UAAQ,OAAM;oBACX,EAAG,8CAA8C,aAAa;UACxD,CAAA;SACF;UACL,EAAA,CAAA;OACN,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,GAAD;SAAQ,SAAQ;SAAU,SAAS;SAAc,WAAU;mBACxD,EAAG,2CAA2C,gBAAgB;SACxD,CAAA;QACL,CAAA;OACF;;KACM,CAAA;IACT,CAAA;GAIR,EAAkB,SAAS,IAC1B,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAkB,KAAK,MACtB,kBAAC,GAAD;KAA6B,WAAU;KAAQ,WAAU;eAAzD;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,QAAD;UAAM,WAAU;oBAA+B,EAAS,KAAK,OAAO,EAAE;UAAQ,CAAA;SAC1E,CAAA;QACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,MAAD;UAAI,WAAU;oBAA0B,EAAS;UAAkB,CAAA,EACnE,kBAAC,KAAD;UAAG,WAAU;oBAAwC,EAAS;UAAS,CAAA,CACnE;;QACL,EAAS,YACR,kBAAC,GAAD;SAAa,WAAU;SAAoC,cAAW;SAAa,CAAA;QAEjF;;MAEN,kBAAC,KAAD;OAAG,WAAU;iBAAiD,EAAS;OAAY,CAAA;MAEnF,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACG,EAAS,gBAAgB,MAAM,GAAG,EAAE,CAAC,KAAK,MACzC,kBAAC,GAAD;QAAkB,SAAQ;QAAY,WAAU;kBAC7C;QACK,EAFI,EAEJ,CACR,EACD,EAAS,gBAAgB,SAAS,KACjC,kBAAC,GAAD;QAAO,SAAQ;QAAU,WAAU;kBAAnC,CAA6C,KACzC,EAAS,gBAAgB,SAAS,EAC9B;UAEN;;MAEN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,GAAD,EAAM,WAAU,sDAAuD,CAAA;SACvE,kBAAC,QAAD;UAAM,WAAU;oBAA4B,EAAS,OAAO,QAAQ,EAAE;UAAQ,CAAA;SAC9E,kBAAC,QAAD;UAAM,WAAU;oBAAhB;WAAwD;WACpD,EAAS;WAAa;WACnB;;SACH;WACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD,EAAQ,WAAU,gBAAiB,CAAA,EACnC,kBAAC,QAAD,EAAA,UAAO,EAAS,UAAgB,CAAA,CAC5B;UACF;;MAEN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD,EAAO,WAAU,gBAAiB,CAAA,EACjC,EAAG,+CAA+C,uBAAuB,EACxE,OAAO,EAAS,cACjB,CAAC,CACE;WACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,GAAD,EAAW,WAAU,gBAAiB,CAAA,EACrC,EAAG,2CAA2C,sBAAsB,EACnE,OAAO,EAAS,mBACjB,CAAC,CACE;UACF;;MAEN,kBAAC,GAAD;OAAQ,SAAQ;OAAU,MAAK;OAAK,WAAU;iBAC3C,EAAG,iDAAiD,eAAe;OAC7D,CAAA;MACC;OA7DI,EAAS,GA6Db,CACZ;IACE,CAAA,GAEN,kBAAC,GAAD;IACE,cAAa;IACb,OAAO,EAAG,yCAAyC,qBAAqB;IACxE,aAAa,EACX,8CACA,iDACD;IACD,QACE,kBAAC,GAAD;KAAQ,SAAQ;KAAU,SAAS;eAChC,EAAG,2CAA2C,gBAAgB;KACxD,CAAA;IAEX,CAAA;GAIH,EAAkB,SAAS,KAC1B,kBAAC,GAAD;IAAe,WAAU;cAAzB,CACE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAG,yCAAyC,sBAAsB;MAChE,CAAA;KACD,CAAA,EACN,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,GAAD,EAAM,WAAU,6BAA8B,CAAA;SAC1C,CAAA;QACN,kBAAC,OAAD;SAAK,WAAU;mBAAmC;SAAgB,CAAA;QAClE,kBAAC,OAAD;SAAK,WAAU;mBACZ,EAAG,iDAAiD,iBAAiB;SAClE,CAAA;QACF;;MACN,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,GAAD,EAAO,WAAU,6BAA8B,CAAA;SAC3C,CAAA;QACN,kBAAC,OAAD;SAAK,WAAU;mBAAmC,EAAkB;SAAa,CAAA;QACjF,kBAAC,OAAD;SAAK,WAAU;mBACZ,EAAG,mDAAmD,mBAAmB;SACtE,CAAA;QACF;;MACN,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,OAAD;SAAK,WAAU;mBACb,kBAAC,GAAD,EAAQ,WAAU,6BAA8B,CAAA;SAC5C,CAAA;QACN,kBAAC,OAAD;SAAK,WAAU;mBAAmC;SAAgB,CAAA;QAClE,kBAAC,OAAD;SAAK,WAAU;mBACZ,EAAG,6CAA6C,YAAY;SACzD,CAAA;QACF;;MACF;OACQ;;GAEd"}
|
|
1
|
+
{"version":3,"file":"ProvidersPage.js","names":[],"sources":["../../../src/proserviceworld/pages/ProvidersPage.tsx"],"sourcesContent":["import { useState, useMemo } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell';\nimport {\n Card,\n CardContent,\n Button,\n Badge,\n Input,\n GlassCard,\n EmphasisPanel,\n AuroraBackground,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport {\n Search,\n Filter,\n Star,\n Users,\n MapPin,\n CheckCircle,\n Clock,\n Briefcase,\n} from 'lucide-react';\nimport { useProServiceWorldStore } from '../store/proserviceworldStore';\nimport type { ServiceCategory } from '../types';\nimport { tWithFallback } from '../utils/i18n';\n\nconst categoryOptions: { value: ServiceCategory | ''; key: string; fallback: string }[] = [\n {\n value: '',\n key: 'proserviceworld.shared.filters.allCategories',\n fallback: 'All Categories',\n },\n {\n value: 'business_consulting',\n key: 'proserviceworld.shared.categories.businessConsulting',\n fallback: 'Business Consulting',\n },\n {\n value: 'financial_services',\n key: 'proserviceworld.shared.categories.financialServices',\n fallback: 'Financial Services',\n },\n {\n value: 'legal_services',\n key: 'proserviceworld.shared.categories.legalServices',\n fallback: 'Legal Services',\n },\n {\n value: 'accounting_bookkeeping',\n key: 'proserviceworld.shared.categories.accountingBookkeeping',\n fallback: 'Accounting & Bookkeeping',\n },\n {\n value: 'marketing_advertising',\n key: 'proserviceworld.shared.categories.marketingAdvertising',\n fallback: 'Marketing & Advertising',\n },\n {\n value: 'it_technology',\n key: 'proserviceworld.shared.categories.itTechnology',\n fallback: 'IT & Technology',\n },\n {\n value: 'human_resources',\n key: 'proserviceworld.shared.categories.humanResources',\n fallback: 'Human Resources',\n },\n {\n value: 'creative_design',\n key: 'proserviceworld.shared.categories.creativeDesign',\n fallback: 'Creative & Design',\n },\n {\n value: 'education_training',\n key: 'proserviceworld.shared.categories.educationTraining',\n fallback: 'Education & Training',\n },\n];\n\nexport function ProvidersPage() {\n const { t } = useI18n();\n const tr = (key: string, fallback: string, params?: Record<string, string | number>): string =>\n tWithFallback(t, key, fallback, params);\n const { providers } = useProServiceWorldStore();\n const [searchQuery, setSearchQuery] = useState('');\n const [selectedCategory, setSelectedCategory] = useState<ServiceCategory | ''>('');\n const [minRating, setMinRating] = useState<number | ''>('');\n const [showFilters, setShowFilters] = useState(false);\n\n const filteredProviders = useMemo(() => {\n let result = [...providers];\n\n if (searchQuery) {\n const q = searchQuery.toLowerCase();\n result = result.filter(\n (p) =>\n p.name.toLowerCase().includes(q) ||\n p.businessName.toLowerCase().includes(q) ||\n p.tagline.toLowerCase().includes(q) ||\n p.specializations.some((s) => s.toLowerCase().includes(q))\n );\n }\n\n if (selectedCategory) {\n result = result.filter((p) => p.category === selectedCategory);\n }\n\n if (minRating) {\n result = result.filter((p) => p.rating >= minRating);\n }\n\n result.sort((a, b) => b.rating - a.rating);\n return result;\n }, [providers, searchQuery, selectedCategory, minRating]);\n\n const avgRating =\n filteredProviders.length > 0\n ? (\n filteredProviders.reduce((sum, p) => sum + p.rating, 0) / filteredProviders.length\n ).toFixed(1)\n : '0';\n\n const countries = new Set(filteredProviders.map((p) => p.country)).size;\n\n const clearFilters = () => {\n setSearchQuery('');\n setSelectedCategory('');\n setMinRating('');\n };\n const subtitle = searchQuery\n ? tr('proserviceworld.providers.subtitleWithQuery', '{{count}} providers found for \"{{query}}\"', {\n count: filteredProviders.length,\n query: searchQuery,\n })\n : tr('proserviceworld.providers.subtitle', '{{count}} providers found', {\n count: filteredProviders.length,\n });\n\n return (\n <div className=\"space-y-6 p-6 bg-bg-canvas min-h-screen\">\n {/* Hero header — the ONE aurora backdrop for this MFE */}\n <div className=\"relative overflow-hidden rounded-card -mx-2 px-2 pt-1\">\n <AuroraBackground intensity=\"subtle\" />\n <div className=\"flex flex-col md:flex-row md:items-center md:justify-between gap-4\">\n <div>\n <h1 className=\"text-2xl font-semibold tracking-tight\">\n <GradientText>{tr('proserviceworld.providers.title', 'Service Providers')}</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">{subtitle}</p>\n </div>\n <Button\n variant=\"outline\"\n onClick={() => setShowFilters(!showFilters)}\n aria-expanded={showFilters}\n >\n <Filter className=\"h-4 w-4 mr-2\" />\n {tr('proserviceworld.providers.actions.filters', 'Filters')}\n </Button>\n </div>\n </div>\n\n <PagePurpose icon={<Users className=\"h-4 w-4\" />}>\n {tr(\n 'proserviceworld.providers.purpose',\n 'Find and vet the experts behind the work. Search by name, business, or specialization and filter by category and rating to shortlist trusted providers — then open a profile to view their services, reviews, and credentials before reaching out.'\n )}\n </PagePurpose>\n\n {/* Search Bar */}\n <div className=\"flex gap-4\">\n <div className=\"flex-1 relative\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-text-secondary\" />\n <Input\n type=\"text\"\n placeholder={tr(\n 'proserviceworld.providers.search.placeholder',\n 'Search providers by name, business, or specialization...'\n )}\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"pl-10 h-12\"\n />\n </div>\n </div>\n\n {/* Filters Panel */}\n {showFilters && (\n <Card className=\"border-border-seam\">\n <CardContent className=\"p-6\">\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-4\">\n <div>\n <label className=\"block text-sm font-medium mb-2\">\n {tr('proserviceworld.providers.filters.category', 'Category')}\n </label>\n <select\n value={selectedCategory}\n onChange={(e) => setSelectedCategory(e.target.value as ServiceCategory | '')}\n className=\"w-full p-2 border border-border-seam rounded-md bg-bg-surface text-text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n {categoryOptions.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {tr(opt.key, opt.fallback)}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"block text-sm font-medium mb-2\">\n {tr('proserviceworld.providers.filters.minimumRating', 'Minimum Rating')}\n </label>\n <select\n value={minRating}\n onChange={(e) => setMinRating(e.target.value ? Number(e.target.value) : '')}\n className=\"w-full p-2 border border-border-seam rounded-md bg-bg-surface text-text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n >\n <option value=\"\">\n {tr('proserviceworld.providers.filters.anyRating', 'Any Rating')}\n </option>\n <option value=\"4\">\n {tr('proserviceworld.providers.filters.rating4', '4+ Stars')}\n </option>\n <option value=\"4.5\">\n {tr('proserviceworld.providers.filters.rating45', '4.5+ Stars')}\n </option>\n <option value=\"4.8\">\n {tr('proserviceworld.providers.filters.rating48', '4.8+ Stars')}\n </option>\n </select>\n </div>\n <div className=\"flex items-end\">\n <Button variant=\"outline\" onClick={clearFilters} className=\"w-full\">\n {tr('proserviceworld.providers.filters.clear', 'Clear Filters')}\n </Button>\n </div>\n </div>\n </CardContent>\n </Card>\n )}\n\n {/* Provider Grid */}\n {filteredProviders.length > 0 ? (\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6\">\n {filteredProviders.map((provider) => (\n <GlassCard key={provider.id} treatment=\"glass\" className=\"border-border-seam transition-shadow hover:shadow-pop\">\n <div className=\"flex items-center space-x-4 mb-4\">\n <div className=\"h-12 w-12 rounded-full bg-accent-soft flex items-center justify-center\">\n <span className=\"text-text-primary font-bold\">{provider.name.charAt(0)}</span>\n </div>\n <div className=\"flex-1 min-w-0\">\n <h3 className=\"font-semibold truncate\">{provider.businessName}</h3>\n <p className=\"text-sm text-text-secondary truncate\">{provider.name}</p>\n </div>\n {provider.verified && (\n <CheckCircle className=\"h-5 w-5 text-accent-blue shrink-0\" aria-label=\"Verified\" />\n )}\n </div>\n\n <p className=\"text-sm text-text-secondary mb-3 line-clamp-2\">{provider.tagline}</p>\n\n <div className=\"flex flex-wrap gap-1 mb-3\">\n {provider.specializations.slice(0, 2).map((spec) => (\n <Badge key={spec} variant=\"secondary\" className=\"text-xs\">\n {spec}\n </Badge>\n ))}\n {provider.specializations.length > 2 && (\n <Badge variant=\"outline\" className=\"text-xs\">\n +{provider.specializations.length - 2}\n </Badge>\n )}\n </div>\n\n <div className=\"flex items-center justify-between text-sm mb-3\">\n <div className=\"flex items-center\">\n <Star className=\"h-4 w-4 text-status-warning-text fill-current mr-1\" />\n <span className=\"font-medium tabular-nums\">{provider.rating.toFixed(1)}</span>\n <span className=\"text-text-secondary ml-1 tabular-nums\">\n ({provider.totalReviews})\n </span>\n </div>\n <div className=\"flex items-center text-text-secondary\">\n <MapPin className=\"h-3 w-3 mr-1\" />\n <span>{provider.location}</span>\n </div>\n </div>\n\n <div className=\"grid grid-cols-2 gap-2 text-xs text-text-secondary border-t border-border-seam pt-3\">\n <div className=\"flex items-center\">\n <Clock className=\"h-3 w-3 mr-1\" />\n {tr('proserviceworld.providers.card.responseTime', '{{count}}h response', {\n count: provider.responseTime,\n })}\n </div>\n <div className=\"flex items-center\">\n <Briefcase className=\"h-3 w-3 mr-1\" />\n {tr('proserviceworld.providers.card.projects', '{{count}} projects', {\n count: provider.projectsCompleted,\n })}\n </div>\n </div>\n\n <Button variant=\"outline\" size=\"sm\" className=\"w-full mt-4\">\n {tr('proserviceworld.providers.actions.viewProfile', 'View Profile')}\n </Button>\n </GlassCard>\n ))}\n </div>\n ) : (\n <IllustratedEmptyState\n illustration=\"empty-people\"\n title={tr('proserviceworld.providers.empty.title', 'No providers found')}\n description={tr(\n 'proserviceworld.shared.empty.adjustFilters',\n 'Try adjusting your search criteria or filters.'\n )}\n action={\n <Button variant=\"outline\" onClick={clearFilters}>\n {tr('proserviceworld.providers.filters.clear', 'Clear Filters')}\n </Button>\n }\n />\n )}\n\n {/* Provider Statistics — the single emphasised insight zone */}\n {filteredProviders.length > 0 && (\n <EmphasisPanel className=\"p-8\">\n <div className=\"text-center mb-8\">\n <h2 className=\"text-xl font-bold tracking-tight\">\n {tr('proserviceworld.providers.stats.title', 'Provider Statistics')}\n </h2>\n </div>\n <div className=\"grid grid-cols-1 md:grid-cols-3 gap-8\">\n <div className=\"text-center\">\n <div className=\"flex items-center justify-center w-12 h-12 mx-auto bg-accent-soft rounded-lg mb-4\">\n <Star className=\"h-6 w-6 text-text-primary\" />\n </div>\n <div className=\"text-2xl font-bold tabular-nums\">{avgRating}</div>\n <div className=\"text-sm text-text-secondary\">\n {tr('proserviceworld.providers.stats.averageRating', 'Average Rating')}\n </div>\n </div>\n <div className=\"text-center\">\n <div className=\"flex items-center justify-center w-12 h-12 mx-auto bg-accent-soft rounded-lg mb-4\">\n <Users className=\"h-6 w-6 text-text-primary\" />\n </div>\n <div className=\"text-2xl font-bold tabular-nums\">{filteredProviders.length}</div>\n <div className=\"text-sm text-text-secondary\">\n {tr('proserviceworld.providers.stats.expertProviders', 'Expert Providers')}\n </div>\n </div>\n <div className=\"text-center\">\n <div className=\"flex items-center justify-center w-12 h-12 mx-auto bg-accent-soft rounded-lg mb-4\">\n <MapPin className=\"h-6 w-6 text-text-primary\" />\n </div>\n <div className=\"text-2xl font-bold tabular-nums\">{countries}</div>\n <div className=\"text-sm text-text-secondary\">\n {tr('proserviceworld.providers.stats.countries', 'Countries')}\n </div>\n </div>\n </div>\n </EmphasisPanel>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|