@burdenoff/microfe-crewfoundry 2026.704.1 → 2026.704.3
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/crewfoundry/components/forms/FormModal.js.map +1 -1
- package/dist/crewfoundry/pages/AdminAdminPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/AnalyticsAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/AnalyticsEmployeeAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/AnalyticsFinancialAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/AnalyticsHiringAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsAnnouncementsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsCallsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsDirectoryPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsFilesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsMessagesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsTeamsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsVideoCallsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CompanyClientManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CompanyCompanyDashboardPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CompanyCompanyProfilePagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CompanyVendorManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/DashboardAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/DashboardDashboardPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/DashboardEnhancedDashboardPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/DashboardExecutiveDashboardPage.js.map +1 -1
- package/dist/crewfoundry/pages/HelpApiDocumentationPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringApplicationsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringCandidatesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringCreateJobPagePage.js +1 -1
- package/dist/crewfoundry/pages/HiringCreateJobPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringInterviewsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringJobDetailsPagePage.js +1 -1
- package/dist/crewfoundry/pages/HiringJobDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringJobsPagePage.js +2 -2
- package/dist/crewfoundry/pages/HiringJobsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrDepartmentsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrEmployeesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrLeaveManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrPayrollPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrTimeTrackingPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningAssessmentsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCertificatesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCourseDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCoursesPagePage.js +1 -1
- package/dist/crewfoundry/pages/LearningCoursesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningLearningPagePage.js +1 -1
- package/dist/crewfoundry/pages/LearningLearningPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningLearningPathDetailPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningLearningPathsPagePage.js +1 -1
- package/dist/crewfoundry/pages/LearningLearningPathsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningMyLearningPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceBookingManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceEarningsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePageEnhancedPage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceMarketplacePagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceMarketplaceServicesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceMessagingPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceMyServicesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceProjectDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceServiceDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/PaymentsEscrowPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/PaymentsInvoicesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/PaymentsPaymentDashboardPagePage.js +1 -1
- package/dist/crewfoundry/pages/PaymentsPaymentDashboardPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/PaymentsPaymentMethodsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/PaymentsSecurityPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/ProjectsCreateProjectPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/ProjectsProjectsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamDetailPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamDirectoryPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamSettingsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamsPagePage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LearningMyLearningPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/LearningMyLearningPagePage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { Award, BookOpen, Clock, Play, Star } from 'lucide-react';\nimport {\n Button,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n Pagination,\n} from '@burdenoff/fe-libs/ui';\nimport { usePagination } from '@burdenoff/fe-libs/shared/hooks';\nimport { useGlobalUi } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useCrewFoundryStore } from '../store/crewfoundryStore';\n\ntype EnrolledCourse = {\n id: string;\n title: string;\n progress: number;\n modules: number;\n completedModules: number;\n instructor: string;\n};\n\ntype CompletedCourse = {\n id: string;\n title: string;\n rating: number;\n};\n\nexport function LearningMyLearningPagePage() {\n const { courses, enrollCourse } = useCrewFoundryStore();\n const { showToast } = useGlobalUi();\n const [enrollingId, setEnrollingId] = useState<string | null>(null);\n const inProgressPaging = usePagination({ initialPageSize: 6 });\n const completedPaging = usePagination({ initialPageSize: 6 });\n\n const handleContinue = async (id: string) => {\n setEnrollingId(id);\n try {\n await enrollCourse(id);\n showToast('Enrollment updated', 'success');\n } catch (error) {\n showToast(error instanceof Error ? error.message : 'Unable to update course', 'error');\n } finally {\n setEnrollingId(null);\n }\n };\n\n const myEnrolledCourses = useMemo<EnrolledCourse[]>(() => {\n return courses\n .filter((course) => course.status === 'active')\n .map((course) => {\n const completedModules = Math.min(\n course.modules,\n Math.round((course.completed / Math.max(course.enrolled, 1)) * course.modules)\n );\n const progress = Math.round((completedModules / Math.max(course.modules, 1)) * 100);\n return {\n id: course.id,\n title: course.title,\n progress,\n modules: course.modules,\n completedModules,\n instructor: course.instructor,\n };\n });\n }, [courses]);\n\n const completedCourses = useMemo<CompletedCourse[]>(() => {\n return courses\n .filter((course) => course.completed > 0)\n .map((course) => ({\n id: `completed-${course.id}`,\n title: course.title,\n rating: Number(course.rating.toFixed(1)),\n }));\n }, [courses]);\n\n const hoursLogged = myEnrolledCourses.reduce(\n (sum, course) => sum + course.completedModules * 1.25,\n 0\n );\n\n const pagedEnrolled = myEnrolledCourses.slice(\n inProgressPaging.offset,\n inProgressPaging.offset + inProgressPaging.pageSize\n );\n const pagedCompleted = completedCourses.slice(\n completedPaging.offset,\n completedPaging.offset + completedPaging.pageSize\n );\n\n return (\n <div className=\"space-y-6 p-6\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>My Learning</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">Track your learning progress and achievements</p>\n <PagePurpose className=\"mt-1\">\n Everything you're currently learning — track your enrolled courses and progress all in\n one place.\n </PagePurpose>\n </div>\n\n <div className=\"grid gap-4 sm:grid-cols-3\">\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">In Progress</CardTitle>\n <BookOpen className=\"h-4 w-4 text-status-info-text\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{myEnrolledCourses.length}</div>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Completed</CardTitle>\n <Award className=\"h-4 w-4 text-status-success-text\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{completedCourses.length}</div>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Hours Logged</CardTitle>\n <Clock className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{hoursLogged.toFixed(1)}</div>\n </CardContent>\n </GlassCard>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>In Progress</CardTitle>\n <CardDescription>Continue where you left off</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {myEnrolledCourses.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title=\"Nothing in progress yet\"\n description=\"Courses you enroll in will show up here so you can pick up where you left off.\"\n />\n ) : (\n pagedEnrolled.map((course) => (\n <div key={course.id} className=\"flex items-center gap-4 rounded border p-4\">\n <div className=\"flex-1\">\n <p className=\"text-sm font-medium\">{course.title}</p>\n <p className=\"text-xs text-text-secondary\">{course.instructor}</p>\n <div className=\"mt-2 flex items-center gap-2\">\n <div className=\"h-2 flex-1 overflow-hidden rounded bg-bg-sunken\">\n <div\n className=\"h-2 rounded bg-action-primary-bg\"\n style={{ width: `${course.progress}%` }}\n />\n </div>\n <span className=\"text-xs font-medium\">{course.progress}%</span>\n </div>\n <p className=\"mt-1 text-xs text-text-secondary\">\n {course.completedModules}/{course.modules} modules\n </p>\n </div>\n <Button\n size=\"sm\"\n onClick={() => handleContinue(course.id)}\n disabled={enrollingId === course.id}\n >\n <Play className=\"mr-1 h-3 w-3\" />\n {enrollingId === course.id ? 'Saving…' : 'Continue'}\n </Button>\n </div>\n ))\n )}\n {myEnrolledCourses.length > inProgressPaging.pageSize && (\n <Pagination\n page={inProgressPaging.page}\n pageSize={inProgressPaging.pageSize}\n totalItems={myEnrolledCourses.length}\n onPageChange={inProgressPaging.setPage}\n />\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Completed</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {completedCourses.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title=\"No completed courses yet\"\n description=\"Courses your team finishes will be listed here.\"\n />\n ) : (\n pagedCompleted.map((course) => (\n <div key={course.id} className=\"flex items-center justify-between rounded border p-3\">\n <div>\n <p className=\"text-sm font-medium\">{course.title}</p>\n </div>\n <div className=\"flex items-center gap-1\">\n <Star className=\"h-3 w-3 text-status-warning-text\" />\n {course.rating}/5\n </div>\n </div>\n ))\n )}\n {completedCourses.length > completedPaging.pageSize && (\n <Pagination\n page={completedPaging.page}\n pageSize={completedPaging.pageSize}\n totalItems={completedCourses.length}\n onPageChange={completedPaging.setPage}\n />\n )}\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"LearningMyLearningPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/LearningMyLearningPagePage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { Award, BookOpen, Clock, Play, Star } from 'lucide-react';\nimport {\n Button,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n Pagination,\n} from '@burdenoff/fe-libs/ui';\nimport { usePagination } from '@burdenoff/fe-libs/shared/hooks';\nimport { useGlobalUi } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useCrewFoundryStore } from '../store/crewfoundryStore';\n\ntype EnrolledCourse = {\n id: string;\n title: string;\n progress: number;\n modules: number;\n completedModules: number;\n instructor: string;\n};\n\ntype CompletedCourse = {\n id: string;\n title: string;\n rating: number;\n};\n\nexport function LearningMyLearningPagePage() {\n const { courses, enrollCourse } = useCrewFoundryStore();\n const { showToast } = useGlobalUi();\n const [enrollingId, setEnrollingId] = useState<string | null>(null);\n const inProgressPaging = usePagination({ initialPageSize: 6 });\n const completedPaging = usePagination({ initialPageSize: 6 });\n\n const handleContinue = async (id: string) => {\n setEnrollingId(id);\n try {\n await enrollCourse(id);\n showToast('Enrollment updated', 'success');\n } catch (error) {\n showToast(error instanceof Error ? error.message : 'Unable to update course', 'error');\n } finally {\n setEnrollingId(null);\n }\n };\n\n const myEnrolledCourses = useMemo<EnrolledCourse[]>(() => {\n return courses\n .filter((course) => course.status === 'active')\n .map((course) => {\n const completedModules = Math.min(\n course.modules,\n Math.round((course.completed / Math.max(course.enrolled, 1)) * course.modules)\n );\n const progress = Math.round((completedModules / Math.max(course.modules, 1)) * 100);\n return {\n id: course.id,\n title: course.title,\n progress,\n modules: course.modules,\n completedModules,\n instructor: course.instructor,\n };\n });\n }, [courses]);\n\n const completedCourses = useMemo<CompletedCourse[]>(() => {\n return courses\n .filter((course) => course.completed > 0)\n .map((course) => ({\n id: `completed-${course.id}`,\n title: course.title,\n rating: Number(course.rating.toFixed(1)),\n }));\n }, [courses]);\n\n const hoursLogged = myEnrolledCourses.reduce(\n (sum, course) => sum + course.completedModules * 1.25,\n 0\n );\n\n const pagedEnrolled = myEnrolledCourses.slice(\n inProgressPaging.offset,\n inProgressPaging.offset + inProgressPaging.pageSize\n );\n const pagedCompleted = completedCourses.slice(\n completedPaging.offset,\n completedPaging.offset + completedPaging.pageSize\n );\n\n return (\n <div className=\"space-y-6 p-6\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>My Learning</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">Track your learning progress and achievements</p>\n <PagePurpose className=\"mt-1\">\n Everything you're currently learning — track your enrolled courses and progress all\n in one place.\n </PagePurpose>\n </div>\n\n <div className=\"grid gap-4 sm:grid-cols-3\">\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">In Progress</CardTitle>\n <BookOpen className=\"h-4 w-4 text-status-info-text\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{myEnrolledCourses.length}</div>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Completed</CardTitle>\n <Award className=\"h-4 w-4 text-status-success-text\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{completedCourses.length}</div>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Hours Logged</CardTitle>\n <Clock className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{hoursLogged.toFixed(1)}</div>\n </CardContent>\n </GlassCard>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>In Progress</CardTitle>\n <CardDescription>Continue where you left off</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {myEnrolledCourses.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title=\"Nothing in progress yet\"\n description=\"Courses you enroll in will show up here so you can pick up where you left off.\"\n />\n ) : (\n pagedEnrolled.map((course) => (\n <div key={course.id} className=\"flex items-center gap-4 rounded border p-4\">\n <div className=\"flex-1\">\n <p className=\"text-sm font-medium\">{course.title}</p>\n <p className=\"text-xs text-text-secondary\">{course.instructor}</p>\n <div className=\"mt-2 flex items-center gap-2\">\n <div className=\"h-2 flex-1 overflow-hidden rounded bg-bg-sunken\">\n <div\n className=\"h-2 rounded bg-action-primary-bg\"\n style={{ width: `${course.progress}%` }}\n />\n </div>\n <span className=\"text-xs font-medium\">{course.progress}%</span>\n </div>\n <p className=\"mt-1 text-xs text-text-secondary\">\n {course.completedModules}/{course.modules} modules\n </p>\n </div>\n <Button\n size=\"sm\"\n onClick={() => handleContinue(course.id)}\n disabled={enrollingId === course.id}\n >\n <Play className=\"mr-1 h-3 w-3\" />\n {enrollingId === course.id ? 'Saving…' : 'Continue'}\n </Button>\n </div>\n ))\n )}\n {myEnrolledCourses.length > inProgressPaging.pageSize && (\n <Pagination\n page={inProgressPaging.page}\n pageSize={inProgressPaging.pageSize}\n totalItems={myEnrolledCourses.length}\n onPageChange={inProgressPaging.setPage}\n />\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Completed</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {completedCourses.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title=\"No completed courses yet\"\n description=\"Courses your team finishes will be listed here.\"\n />\n ) : (\n pagedCompleted.map((course) => (\n <div key={course.id} className=\"flex items-center justify-between rounded border p-3\">\n <div>\n <p className=\"text-sm font-medium\">{course.title}</p>\n </div>\n <div className=\"flex items-center gap-1\">\n <Star className=\"h-3 w-3 text-status-warning-text\" />\n {course.rating}/5\n </div>\n </div>\n ))\n )}\n {completedCourses.length > completedPaging.pageSize && (\n <Pagination\n page={completedPaging.page}\n pageSize={completedPaging.pageSize}\n totalItems={completedCourses.length}\n onPageChange={completedPaging.setPage}\n />\n )}\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MarketplaceBookingManagementPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/MarketplaceBookingManagementPagePage.tsx"],"sourcesContent":["import {\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { Calendar, CheckCircle, Clock, Plus } from 'lucide-react';\n\nexport function MarketplaceBookingManagementPagePage() {\n // There is no booking data source or booking-creation mutation yet, so we do\n // not synthesize pseudo-bookings from unrelated services. Counts are a true\n // zero and the list shows an honest empty state until the backend ships.\n const upcomingCount = 0;\n const confirmedCount = 0;\n const pendingCount = 0;\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>Booking Management</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">Manage service provider appointments</p>\n <PagePurpose className=\"mt-1\"
|
|
1
|
+
{"version":3,"file":"MarketplaceBookingManagementPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/MarketplaceBookingManagementPagePage.tsx"],"sourcesContent":["import {\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { Calendar, CheckCircle, Clock, Plus } from 'lucide-react';\n\nexport function MarketplaceBookingManagementPagePage() {\n // There is no booking data source or booking-creation mutation yet, so we do\n // not synthesize pseudo-bookings from unrelated services. Counts are a true\n // zero and the list shows an honest empty state until the backend ships.\n const upcomingCount = 0;\n const confirmedCount = 0;\n const pendingCount = 0;\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>Booking Management</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">Manage service provider appointments</p>\n <PagePurpose className=\"mt-1\">\n Manage your service bookings — review upcoming engagements, confirm schedules, and keep\n your client commitments on track.\n </PagePurpose>\n </div>\n {/* Bookings derive from marketplace services; there is no standalone\n booking-creation mutation yet, so this stays an honest disabled\n control instead of a silently-dead button. */}\n <Button size=\"sm\" disabled title=\"Booking creation is coming soon\">\n <Plus className=\"mr-1 h-4 w-4\" />\n New Booking · Coming soon\n </Button>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-3\">\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Upcoming</CardTitle>\n <Calendar className=\"h-4 w-4 text-status-info-text\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{upcomingCount}</div>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Confirmed</CardTitle>\n <CheckCircle className=\"h-4 w-4 text-status-success-text\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{confirmedCount}</div>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Pending</CardTitle>\n <Clock className=\"h-4 w-4 text-status-warning-text\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{pendingCount}</div>\n </CardContent>\n </GlassCard>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>All Bookings</CardTitle>\n </CardHeader>\n <CardContent>\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title=\"No bookings yet\"\n description=\"Once booking creation ships, your scheduled service appointments will appear here.\"\n />\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":";;;;AAaA,SAAgB,IAAuC;AAQrD,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD,EAAA,UAAA;KACE,kBAAC,MAAD;MAAI,WAAU;gBACZ,kBAAC,GAAD,EAAA,UAAc,sBAAiC,CAAA;MAC5C,CAAA;KACL,kBAAC,KAAD;MAAG,WAAU;gBAA8B;MAAwC,CAAA;KACnF,kBAAC,GAAD;MAAa,WAAU;gBAAO;MAGhB,CAAA;KACV,EAAA,CAAA,EAIN,kBAAC,GAAD;KAAQ,MAAK;KAAK,UAAA;KAAS,OAAM;eAAjC,CACE,kBAAC,GAAD,EAAM,WAAU,gBAAiB,CAAA,EAAA,4BAE1B;OACL;;GAEN,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD;MAAW,WAAU;MAAQ,MAAA;MAAK,WAAU;gBAA5C,CACE,kBAAC,GAAD;OAAY,WAAU;iBAAtB,CACE,kBAAC,GAAD;QAAW,WAAU;kBAAsB;QAAoB,CAAA,EAC/D,kBAAC,GAAD,EAAU,WAAU,iCAAkC,CAAA,CAC3C;UACb,kBAAC,GAAD,EAAA,UACE,kBAAC,OAAD;OAAK,WAAU;iBAjCH;OAiC0D,CAAA,EAC1D,CAAA,CACJ;;KACZ,kBAAC,GAAD;MAAW,WAAU;MAAS,WAAU;gBAAxC,CACE,kBAAC,GAAD;OAAY,WAAU;iBAAtB,CACE,kBAAC,GAAD;QAAW,WAAU;kBAAsB;QAAqB,CAAA,EAChE,kBAAC,GAAD,EAAa,WAAU,oCAAqC,CAAA,CACjD;UACb,kBAAC,GAAD,EAAA,UACE,kBAAC,OAAD;OAAK,WAAU;iBAzCF;OAyC0D,CAAA,EAC3D,CAAA,CACJ;;KACZ,kBAAC,GAAD;MAAW,WAAU;MAAS,WAAU;gBAAxC,CACE,kBAAC,GAAD;OAAY,WAAU;iBAAtB,CACE,kBAAC,GAAD;QAAW,WAAU;kBAAsB;QAAmB,CAAA,EAC9D,kBAAC,GAAD,EAAO,WAAU,oCAAqC,CAAA,CAC3C;UACb,kBAAC,GAAD,EAAA,UACE,kBAAC,OAAD;OAAK,WAAU;iBAjDJ;OAiD0D,CAAA,EACzD,CAAA,CACJ;;KACR;;GAEN,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAW,gBAAwB,CAAA,EACxB,CAAA,EACb,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;IACE,cAAa;IACb,OAAM;IACN,aAAY;IACZ,CAAA,EACU,CAAA,CACT,EAAA,CAAA;GACH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MarketplaceEarningsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/MarketplaceEarningsPagePage.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport {\n Badge,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { DollarSign, TrendingUp, Wallet } from 'lucide-react';\nimport { useCrewFoundryStore } from '../store/crewfoundryStore';\n\ntype EarningPoint = {\n month: string;\n amount: number;\n orders: number;\n};\n\ntype PaymentItem = {\n id: string;\n client: string;\n amount: number;\n date: string;\n status: 'completed' | 'pending';\n};\n\nconst monthLabel = (value: string): string => {\n const parsed = new Date(value);\n if (Number.isNaN(parsed.getTime())) {\n return 'N/A';\n }\n return parsed.toLocaleString('en-US', { month: 'short' });\n};\n\nconst isoDate = (value: string): string => {\n const parsed = new Date(value);\n if (Number.isNaN(parsed.getTime())) {\n return 'Unknown';\n }\n return parsed.toISOString().split('T')[0];\n};\n\nexport function MarketplaceEarningsPagePage() {\n const { invoices } = useCrewFoundryStore();\n\n const recentPayments = useMemo<PaymentItem[]>(() => {\n return invoices.slice(0, 12).map((invoice) => ({\n id: invoice.id,\n client: invoice.clientName,\n amount: invoice.amount,\n date: isoDate(invoice.issueDate),\n status: invoice.status === 'paid' ? 'completed' : 'pending',\n }));\n }, [invoices]);\n\n const earningsData = useMemo<EarningPoint[]>(() => {\n return recentPayments.slice(0, 6).map((payment, index) => {\n const sibling = recentPayments.filter(\n (item) => monthLabel(item.date) === monthLabel(payment.date)\n );\n const amount = sibling.reduce((sum, item) => sum + item.amount, 0);\n return {\n month: monthLabel(payment.date) || `M${index + 1}`,\n amount,\n orders: sibling.length,\n };\n });\n }, [recentPayments]);\n\n const totalEarnings = earningsData.reduce((sum, point) => sum + point.amount, 0);\n const maxEarning = Math.max(1, ...earningsData.map((point) => point.amount));\n const totalOrders = earningsData.reduce((sum, point) => sum + point.orders, 0);\n const pendingAmount = recentPayments\n .filter((payment) => payment.status === 'pending')\n .reduce((sum, payment) => sum + payment.amount, 0);\n\n return (\n <div className=\"space-y-6 p-6\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>Earnings</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">Revenue from marketplace services</p>\n <PagePurpose className=\"mt-1\">Track what you've earned on the marketplace — payouts, pending balances, and earning trends so you always know where you stand.</PagePurpose>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-3\">\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Total Earnings</CardTitle>\n <DollarSign className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">${totalEarnings.toLocaleString()}</div>\n <p className=\"text-xs text-text-secondary\">Across {totalOrders} paid orders</p>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Avg Order Value</CardTitle>\n <TrendingUp className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n ${Math.round(totalEarnings / Math.max(totalOrders, 1)).toLocaleString()}\n </div>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Pending</CardTitle>\n <Wallet className=\"h-4 w-4 text-status-warning-text\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">${pendingAmount.toLocaleString()}</div>\n </CardContent>\n </GlassCard>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>Earnings Trend</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {earningsData.map((earning, index) => (\n <div key={`${earning.month}-${index}`} className=\"flex items-center gap-3\">\n <span className=\"w-10 text-sm text-text-secondary\">{earning.month}</span>\n <div className=\"h-4 flex-1 overflow-hidden rounded bg-bg-sunken\">\n <div\n className=\"h-4 rounded bg-action-primary-bg\"\n style={{ width: `${Math.round((earning.amount / maxEarning) * 100)}%` }}\n />\n </div>\n <span className=\"w-16 text-right text-sm font-medium\">\n ${(earning.amount / 1000).toFixed(1)}K\n </span>\n <Badge variant=\"outline\" className=\"text-[10px]\">\n {earning.orders} orders\n </Badge>\n </div>\n ))}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Recent Payments</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {recentPayments.map((payment) => (\n <div key={payment.id} className=\"flex items-center justify-between rounded border p-3\">\n <div>\n <p className=\"text-sm font-medium\">{payment.client}</p>\n <p className=\"text-xs text-text-secondary\">{payment.date}</p>\n </div>\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm font-bold\">${payment.amount.toLocaleString()}</span>\n <Badge\n variant={payment.status === 'completed' ? 'default' : 'secondary'}\n className=\"text-[10px]\"\n >\n {payment.status}\n </Badge>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MarketplaceEarningsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/MarketplaceEarningsPagePage.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport {\n Badge,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { DollarSign, TrendingUp, Wallet } from 'lucide-react';\nimport { useCrewFoundryStore } from '../store/crewfoundryStore';\n\ntype EarningPoint = {\n month: string;\n amount: number;\n orders: number;\n};\n\ntype PaymentItem = {\n id: string;\n client: string;\n amount: number;\n date: string;\n status: 'completed' | 'pending';\n};\n\nconst monthLabel = (value: string): string => {\n const parsed = new Date(value);\n if (Number.isNaN(parsed.getTime())) {\n return 'N/A';\n }\n return parsed.toLocaleString('en-US', { month: 'short' });\n};\n\nconst isoDate = (value: string): string => {\n const parsed = new Date(value);\n if (Number.isNaN(parsed.getTime())) {\n return 'Unknown';\n }\n return parsed.toISOString().split('T')[0];\n};\n\nexport function MarketplaceEarningsPagePage() {\n const { invoices } = useCrewFoundryStore();\n\n const recentPayments = useMemo<PaymentItem[]>(() => {\n return invoices.slice(0, 12).map((invoice) => ({\n id: invoice.id,\n client: invoice.clientName,\n amount: invoice.amount,\n date: isoDate(invoice.issueDate),\n status: invoice.status === 'paid' ? 'completed' : 'pending',\n }));\n }, [invoices]);\n\n const earningsData = useMemo<EarningPoint[]>(() => {\n return recentPayments.slice(0, 6).map((payment, index) => {\n const sibling = recentPayments.filter(\n (item) => monthLabel(item.date) === monthLabel(payment.date)\n );\n const amount = sibling.reduce((sum, item) => sum + item.amount, 0);\n return {\n month: monthLabel(payment.date) || `M${index + 1}`,\n amount,\n orders: sibling.length,\n };\n });\n }, [recentPayments]);\n\n const totalEarnings = earningsData.reduce((sum, point) => sum + point.amount, 0);\n const maxEarning = Math.max(1, ...earningsData.map((point) => point.amount));\n const totalOrders = earningsData.reduce((sum, point) => sum + point.orders, 0);\n const pendingAmount = recentPayments\n .filter((payment) => payment.status === 'pending')\n .reduce((sum, payment) => sum + payment.amount, 0);\n\n return (\n <div className=\"space-y-6 p-6\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>Earnings</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">Revenue from marketplace services</p>\n <PagePurpose className=\"mt-1\">\n Track what you've earned on the marketplace — payouts, pending balances, and earning\n trends so you always know where you stand.\n </PagePurpose>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-3\">\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Total Earnings</CardTitle>\n <DollarSign className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">${totalEarnings.toLocaleString()}</div>\n <p className=\"text-xs text-text-secondary\">Across {totalOrders} paid orders</p>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Avg Order Value</CardTitle>\n <TrendingUp className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n ${Math.round(totalEarnings / Math.max(totalOrders, 1)).toLocaleString()}\n </div>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Pending</CardTitle>\n <Wallet className=\"h-4 w-4 text-status-warning-text\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">${pendingAmount.toLocaleString()}</div>\n </CardContent>\n </GlassCard>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>Earnings Trend</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {earningsData.map((earning, index) => (\n <div key={`${earning.month}-${index}`} className=\"flex items-center gap-3\">\n <span className=\"w-10 text-sm text-text-secondary\">{earning.month}</span>\n <div className=\"h-4 flex-1 overflow-hidden rounded bg-bg-sunken\">\n <div\n className=\"h-4 rounded bg-action-primary-bg\"\n style={{ width: `${Math.round((earning.amount / maxEarning) * 100)}%` }}\n />\n </div>\n <span className=\"w-16 text-right text-sm font-medium\">\n ${(earning.amount / 1000).toFixed(1)}K\n </span>\n <Badge variant=\"outline\" className=\"text-[10px]\">\n {earning.orders} orders\n </Badge>\n </div>\n ))}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Recent Payments</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {recentPayments.map((payment) => (\n <div key={payment.id} className=\"flex items-center justify-between rounded border p-3\">\n <div>\n <p className=\"text-sm font-medium\">{payment.client}</p>\n <p className=\"text-xs text-text-secondary\">{payment.date}</p>\n </div>\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm font-bold\">${payment.amount.toLocaleString()}</span>\n <Badge\n variant={payment.status === 'completed' ? 'default' : 'secondary'}\n className=\"text-[10px]\"\n >\n {payment.status}\n </Badge>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MarketplaceFreelancerProfilePageEnhancedPage.js","names":[],"sources":["../../../src/crewfoundry/pages/MarketplaceFreelancerProfilePageEnhancedPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n NextSteps,\n PagePurpose,\n Separator,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport {\n Briefcase,\n Calendar,\n DollarSign,\n MessageSquare,\n Star,\n TrendingUp,\n Video,\n} from 'lucide-react';\nimport { useLocation, useNavigate } from 'react-router-dom';\nimport { useCrewFoundryStore } from '../store/crewfoundryStore';\n\nconst initialsOf = (name: string): string =>\n name\n .split(' ')\n .map((part) => part[0] ?? '')\n .join('')\n .slice(0, 2)\n .toUpperCase() || '—';\n\nexport function MarketplaceFreelancerProfilePageEnhancedPage() {\n const [selectedTab, setSelectedTab] = useState('overview');\n const navigate = useNavigate();\n const location = useLocation();\n const { marketplaceServices } = useCrewFoundryStore();\n const selectedId = new URLSearchParams(location.search).get('id');\n\n const baseService =\n marketplaceServices.find((service) => service.id === selectedId) ?? marketplaceServices[0];\n const providerName = baseService?.provider ?? '';\n\n const providerServices = useMemo(\n () => marketplaceServices.filter((service) => service.provider === providerName),\n [marketplaceServices, providerName]\n );\n\n const skills = useMemo(\n () => Array.from(new Set(providerServices.flatMap((service) => service.skills))).slice(0, 20),\n [providerServices]\n );\n\n const categories = useMemo(\n () => Array.from(new Set(providerServices.map((service) => service.category))),\n [providerServices]\n );\n\n const services = useMemo(\n () =>\n providerServices.map((service) => ({\n id: service.id,\n title: service.title,\n price:\n service.priceType === 'hourly'\n ? `$${service.price}/hr`\n : service.priceType === 'monthly'\n ? `$${service.price}/mo`\n : `$${service.price}`,\n type: service.priceType,\n description: service.description,\n delivery: service.deliveryTime,\n rating: service.rating,\n reviews: service.reviews,\n })),\n [providerServices]\n );\n\n const avgRating =\n providerServices.length > 0\n ? (\n providerServices.reduce((sum, service) => sum + service.rating, 0) /\n providerServices.length\n ).toFixed(1)\n : '0.0';\n const totalReviews = providerServices.reduce((sum, service) => sum + service.reviews, 0);\n const startingRate =\n providerServices.length > 0\n ? Math.min(...providerServices.map((service) => service.price))\n : 0;\n const isTopRated = Number(avgRating) >= 4.5 && totalReviews > 0;\n\n const stats = [\n { label: 'Services', value: `${providerServices.length}` },\n { label: 'Avg Rating', value: avgRating },\n { label: 'Total Reviews', value: `${totalReviews}` },\n { label: 'Starting Rate', value: `$${startingRate.toLocaleString()}` },\n ];\n\n if (!baseService) {\n return (\n <div className=\"p-6\">\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"Provider not found\"\n description=\"This freelancer is no longer available on the marketplace.\"\n />\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6 p-6\">\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam\">\n <CardContent className=\"pt-6\">\n <div className=\"flex items-start gap-6\">\n <div className=\"flex h-24 w-24 shrink-0 items-center justify-center rounded-2xl bg-gradient-to-br from-primary to-primary/60 text-action-primary-text text-3xl font-semibold\">\n {initialsOf(providerName)}\n </div>\n <div className=\"flex-1 space-y-2\">\n <div className=\"flex items-center gap-3 flex-wrap\">\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>{providerName}</GradientText>\n </h1>\n {isTopRated ? <Badge>Top Rated</Badge> : null}\n </div>\n <p className=\"text-sm font-medium\">\n {categories.length > 0 ? categories.join(' · ') : 'Marketplace provider'}\n </p>\n <div className=\"flex flex-wrap items-center gap-4 text-xs text-text-secondary\">\n <span className=\"flex items-center gap-1\">\n <Star className=\"h-3 w-3 text-status-warning-text\" /> {avgRating} ({totalReviews}{' '}\n reviews)\n </span>\n <span className=\"flex items-center gap-1\">\n <Briefcase className=\"h-3 w-3\" /> {providerServices.length} services\n </span>\n <span className=\"flex items-center gap-1\">\n <DollarSign className=\"h-3 w-3\" /> from ${startingRate.toLocaleString()}\n </span>\n </div>\n <div className=\"flex gap-2 pt-2 flex-wrap\">\n <Button size=\"sm\" onClick={() => navigate('../../marketplace/messaging-page')}>\n <MessageSquare className=\"h-3 w-3 mr-1\" /> Send Proposal\n </Button>\n {/* No video-intro/call surface exists yet — honest disabled control. */}\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled\n title=\"Video intro is coming soon\"\n aria-label=\"Video intro — coming soon\"\n >\n <Video className=\"h-3 w-3 mr-1\" /> Video Intro · Coming soon\n </Button>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => setSelectedTab('availability')}\n >\n <Calendar className=\"h-3 w-3 mr-1\" /> Schedule Call\n </Button>\n </div>\n <PagePurpose className=\"pt-2\">\n A freelancer's full profile — skills, listed services, and performance — so you\n can vet them thoroughly and decide whether to hire before you reach out.\n </PagePurpose>\n </div>\n </div>\n </CardContent>\n </GlassCard>\n\n <NextSteps\n storageKey=\"crewfoundry-freelancer-profile-enhanced\"\n steps={[\n {\n id: 'services',\n label: 'Review their services',\n description: 'See exactly what this freelancer offers and at what price.',\n icon: <Briefcase className=\"h-4 w-4\" />,\n onClick: () => setSelectedTab('services'),\n },\n {\n id: 'availability',\n label: 'Check availability',\n description: 'Find open slots before you schedule a call.',\n icon: <Calendar className=\"h-4 w-4\" />,\n onClick: () => setSelectedTab('availability'),\n },\n {\n id: 'performance',\n label: 'Inspect performance',\n description: 'Look at ratings and delivery history to reduce risk.',\n icon: <TrendingUp className=\"h-4 w-4\" />,\n onClick: () => setSelectedTab('analytics'),\n },\n ]}\n />\n\n <div className=\"grid gap-4 md:grid-cols-4\">\n {stats.map((stat) => (\n <Card key={stat.label}>\n <CardContent className=\"pt-6\">\n <p className=\"text-xs text-text-secondary\">{stat.label}</p>\n <p className=\"text-lg font-bold\">{stat.value}</p>\n </CardContent>\n </Card>\n ))}\n </div>\n\n <Tabs value={selectedTab} onValueChange={setSelectedTab}>\n <TabsList>\n <TabsTrigger value=\"overview\">Overview</TabsTrigger>\n <TabsTrigger value=\"services\">Services</TabsTrigger>\n <TabsTrigger value=\"availability\">Availability</TabsTrigger>\n <TabsTrigger value=\"analytics\">Performance</TabsTrigger>\n </TabsList>\n\n <TabsContent value=\"overview\" className=\"space-y-6 mt-4\">\n <div className=\"grid gap-6 lg:grid-cols-3\">\n <div className=\"lg:col-span-2 space-y-4\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Summary</CardTitle>\n </CardHeader>\n <CardContent className=\"text-sm text-text-secondary space-y-3\">\n <p>\n {providerName} offers {providerServices.length}{' '}\n {providerServices.length === 1 ? 'service' : 'services'} on the marketplace\n {categories.length > 0 ? ` across ${categories.join(', ')}` : ''}.\n </p>\n <p>\n Across all listings the provider holds an average rating of {avgRating} from{' '}\n {totalReviews} reviews, with rates starting at $\n {startingRate.toLocaleString()}.\n </p>\n </CardContent>\n </Card>\n </div>\n <div className=\"space-y-4\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-sm\">Expertise Areas</CardTitle>\n </CardHeader>\n <CardContent className=\"flex flex-wrap gap-1.5\">\n {skills.length > 0 ? (\n skills.map((s) => (\n <Badge key={s} variant=\"secondary\" className=\"text-xs\">\n {s}\n </Badge>\n ))\n ) : (\n <p className=\"text-xs text-text-secondary\">No skills listed yet.</p>\n )}\n </CardContent>\n </Card>\n </div>\n </div>\n </TabsContent>\n\n <TabsContent value=\"services\" className=\"mt-4\">\n {services.length > 0 ? (\n <div className=\"grid gap-4 md:grid-cols-2\">\n {services.map((svc) => (\n <Card key={svc.id}>\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-sm\">{svc.title}</CardTitle>\n <Badge variant=\"outline\" className=\"text-xs\">\n {svc.type}\n </Badge>\n </div>\n <CardDescription className=\"text-xs\">{svc.description}</CardDescription>\n </CardHeader>\n <CardContent className=\"pt-0\">\n <Separator className=\"mb-3\" />\n <div className=\"flex items-center justify-between\">\n <div className=\"space-y-0.5\">\n <p className=\"text-sm font-semibold\">{svc.price}</p>\n <p className=\"text-[10px] text-text-secondary\">Delivery: {svc.delivery}</p>\n </div>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() =>\n navigate(`../../marketplace/service-details-page?id=${svc.id}`)\n }\n >\n View\n </Button>\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n ) : (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title=\"No services listed\"\n description=\"This provider has not published any services yet.\"\n />\n )}\n </TabsContent>\n\n <TabsContent value=\"availability\" className=\"mt-4\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Availability</CardTitle>\n <CardDescription>Real-time scheduling with this provider</CardDescription>\n </CardHeader>\n <CardContent>\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title=\"Scheduling is coming soon\"\n description=\"Live availability and consultation booking will appear here once the scheduling backend ships. In the meantime, send a message to arrange a time.\"\n action={\n <Button size=\"sm\" onClick={() => navigate('../../marketplace/messaging-page')}>\n <MessageSquare className=\"h-3 w-3 mr-1\" /> Message provider\n </Button>\n }\n />\n </CardContent>\n </Card>\n </TabsContent>\n\n <TabsContent value=\"analytics\" className=\"mt-4\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Service Performance</CardTitle>\n <CardDescription>Ratings and reviews per listed service</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {providerServices.length > 0 ? (\n providerServices.map((service) => (\n <div key={service.id} className=\"flex items-center justify-between\">\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium truncate\">{service.title}</p>\n <p className=\"text-xs text-text-secondary\">{service.category}</p>\n </div>\n <div className=\"flex items-center gap-2 text-sm shrink-0\">\n <Star className=\"h-3.5 w-3.5 text-status-warning-text\" />\n <span className=\"font-semibold\">{service.rating}</span>\n <span className=\"text-xs text-text-secondary\">({service.reviews})</span>\n </div>\n </div>\n ))\n ) : (\n <p className=\"text-sm text-text-secondary\">No performance data yet.</p>\n )}\n </CardContent>\n </Card>\n </TabsContent>\n </Tabs>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MarketplaceFreelancerProfilePageEnhancedPage.js","names":[],"sources":["../../../src/crewfoundry/pages/MarketplaceFreelancerProfilePageEnhancedPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n NextSteps,\n PagePurpose,\n Separator,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport {\n Briefcase,\n Calendar,\n DollarSign,\n MessageSquare,\n Star,\n TrendingUp,\n Video,\n} from 'lucide-react';\nimport { useLocation, useNavigate } from 'react-router-dom';\nimport { useCrewFoundryStore } from '../store/crewfoundryStore';\n\nconst initialsOf = (name: string): string =>\n name\n .split(' ')\n .map((part) => part[0] ?? '')\n .join('')\n .slice(0, 2)\n .toUpperCase() || '—';\n\nexport function MarketplaceFreelancerProfilePageEnhancedPage() {\n const [selectedTab, setSelectedTab] = useState('overview');\n const navigate = useNavigate();\n const location = useLocation();\n const { marketplaceServices } = useCrewFoundryStore();\n const selectedId = new URLSearchParams(location.search).get('id');\n\n const baseService =\n marketplaceServices.find((service) => service.id === selectedId) ?? marketplaceServices[0];\n const providerName = baseService?.provider ?? '';\n\n const providerServices = useMemo(\n () => marketplaceServices.filter((service) => service.provider === providerName),\n [marketplaceServices, providerName]\n );\n\n const skills = useMemo(\n () => Array.from(new Set(providerServices.flatMap((service) => service.skills))).slice(0, 20),\n [providerServices]\n );\n\n const categories = useMemo(\n () => Array.from(new Set(providerServices.map((service) => service.category))),\n [providerServices]\n );\n\n const services = useMemo(\n () =>\n providerServices.map((service) => ({\n id: service.id,\n title: service.title,\n price:\n service.priceType === 'hourly'\n ? `$${service.price}/hr`\n : service.priceType === 'monthly'\n ? `$${service.price}/mo`\n : `$${service.price}`,\n type: service.priceType,\n description: service.description,\n delivery: service.deliveryTime,\n rating: service.rating,\n reviews: service.reviews,\n })),\n [providerServices]\n );\n\n const avgRating =\n providerServices.length > 0\n ? (\n providerServices.reduce((sum, service) => sum + service.rating, 0) /\n providerServices.length\n ).toFixed(1)\n : '0.0';\n const totalReviews = providerServices.reduce((sum, service) => sum + service.reviews, 0);\n const startingRate =\n providerServices.length > 0 ? Math.min(...providerServices.map((service) => service.price)) : 0;\n const isTopRated = Number(avgRating) >= 4.5 && totalReviews > 0;\n\n const stats = [\n { label: 'Services', value: `${providerServices.length}` },\n { label: 'Avg Rating', value: avgRating },\n { label: 'Total Reviews', value: `${totalReviews}` },\n { label: 'Starting Rate', value: `$${startingRate.toLocaleString()}` },\n ];\n\n if (!baseService) {\n return (\n <div className=\"p-6\">\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"Provider not found\"\n description=\"This freelancer is no longer available on the marketplace.\"\n />\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6 p-6\">\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam\">\n <CardContent className=\"pt-6\">\n <div className=\"flex items-start gap-6\">\n <div className=\"flex h-24 w-24 shrink-0 items-center justify-center rounded-2xl bg-gradient-to-br from-primary to-primary/60 text-action-primary-text text-3xl font-semibold\">\n {initialsOf(providerName)}\n </div>\n <div className=\"flex-1 space-y-2\">\n <div className=\"flex items-center gap-3 flex-wrap\">\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>{providerName}</GradientText>\n </h1>\n {isTopRated ? <Badge>Top Rated</Badge> : null}\n </div>\n <p className=\"text-sm font-medium\">\n {categories.length > 0 ? categories.join(' · ') : 'Marketplace provider'}\n </p>\n <div className=\"flex flex-wrap items-center gap-4 text-xs text-text-secondary\">\n <span className=\"flex items-center gap-1\">\n <Star className=\"h-3 w-3 text-status-warning-text\" /> {avgRating} ({totalReviews}{' '}\n reviews)\n </span>\n <span className=\"flex items-center gap-1\">\n <Briefcase className=\"h-3 w-3\" /> {providerServices.length} services\n </span>\n <span className=\"flex items-center gap-1\">\n <DollarSign className=\"h-3 w-3\" /> from ${startingRate.toLocaleString()}\n </span>\n </div>\n <div className=\"flex gap-2 pt-2 flex-wrap\">\n <Button size=\"sm\" onClick={() => navigate('../../marketplace/messaging-page')}>\n <MessageSquare className=\"h-3 w-3 mr-1\" /> Send Proposal\n </Button>\n {/* No video-intro/call surface exists yet — honest disabled control. */}\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled\n title=\"Video intro is coming soon\"\n aria-label=\"Video intro — coming soon\"\n >\n <Video className=\"h-3 w-3 mr-1\" /> Video Intro · Coming soon\n </Button>\n <Button size=\"sm\" variant=\"outline\" onClick={() => setSelectedTab('availability')}>\n <Calendar className=\"h-3 w-3 mr-1\" /> Schedule Call\n </Button>\n </div>\n <PagePurpose className=\"pt-2\">\n A freelancer's full profile — skills, listed services, and performance — so you\n can vet them thoroughly and decide whether to hire before you reach out.\n </PagePurpose>\n </div>\n </div>\n </CardContent>\n </GlassCard>\n\n <NextSteps\n storageKey=\"crewfoundry-freelancer-profile-enhanced\"\n steps={[\n {\n id: 'services',\n label: 'Review their services',\n description: 'See exactly what this freelancer offers and at what price.',\n icon: <Briefcase className=\"h-4 w-4\" />,\n onClick: () => setSelectedTab('services'),\n },\n {\n id: 'availability',\n label: 'Check availability',\n description: 'Find open slots before you schedule a call.',\n icon: <Calendar className=\"h-4 w-4\" />,\n onClick: () => setSelectedTab('availability'),\n },\n {\n id: 'performance',\n label: 'Inspect performance',\n description: 'Look at ratings and delivery history to reduce risk.',\n icon: <TrendingUp className=\"h-4 w-4\" />,\n onClick: () => setSelectedTab('analytics'),\n },\n ]}\n />\n\n <div className=\"grid gap-4 md:grid-cols-4\">\n {stats.map((stat) => (\n <Card key={stat.label}>\n <CardContent className=\"pt-6\">\n <p className=\"text-xs text-text-secondary\">{stat.label}</p>\n <p className=\"text-lg font-bold\">{stat.value}</p>\n </CardContent>\n </Card>\n ))}\n </div>\n\n <Tabs value={selectedTab} onValueChange={setSelectedTab}>\n <TabsList>\n <TabsTrigger value=\"overview\">Overview</TabsTrigger>\n <TabsTrigger value=\"services\">Services</TabsTrigger>\n <TabsTrigger value=\"availability\">Availability</TabsTrigger>\n <TabsTrigger value=\"analytics\">Performance</TabsTrigger>\n </TabsList>\n\n <TabsContent value=\"overview\" className=\"space-y-6 mt-4\">\n <div className=\"grid gap-6 lg:grid-cols-3\">\n <div className=\"lg:col-span-2 space-y-4\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Summary</CardTitle>\n </CardHeader>\n <CardContent className=\"text-sm text-text-secondary space-y-3\">\n <p>\n {providerName} offers {providerServices.length}{' '}\n {providerServices.length === 1 ? 'service' : 'services'} on the marketplace\n {categories.length > 0 ? ` across ${categories.join(', ')}` : ''}.\n </p>\n <p>\n Across all listings the provider holds an average rating of {avgRating} from{' '}\n {totalReviews} reviews, with rates starting at ${startingRate.toLocaleString()}.\n </p>\n </CardContent>\n </Card>\n </div>\n <div className=\"space-y-4\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-sm\">Expertise Areas</CardTitle>\n </CardHeader>\n <CardContent className=\"flex flex-wrap gap-1.5\">\n {skills.length > 0 ? (\n skills.map((s) => (\n <Badge key={s} variant=\"secondary\" className=\"text-xs\">\n {s}\n </Badge>\n ))\n ) : (\n <p className=\"text-xs text-text-secondary\">No skills listed yet.</p>\n )}\n </CardContent>\n </Card>\n </div>\n </div>\n </TabsContent>\n\n <TabsContent value=\"services\" className=\"mt-4\">\n {services.length > 0 ? (\n <div className=\"grid gap-4 md:grid-cols-2\">\n {services.map((svc) => (\n <Card key={svc.id}>\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-sm\">{svc.title}</CardTitle>\n <Badge variant=\"outline\" className=\"text-xs\">\n {svc.type}\n </Badge>\n </div>\n <CardDescription className=\"text-xs\">{svc.description}</CardDescription>\n </CardHeader>\n <CardContent className=\"pt-0\">\n <Separator className=\"mb-3\" />\n <div className=\"flex items-center justify-between\">\n <div className=\"space-y-0.5\">\n <p className=\"text-sm font-semibold\">{svc.price}</p>\n <p className=\"text-[10px] text-text-secondary\">Delivery: {svc.delivery}</p>\n </div>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() =>\n navigate(`../../marketplace/service-details-page?id=${svc.id}`)\n }\n >\n View\n </Button>\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n ) : (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title=\"No services listed\"\n description=\"This provider has not published any services yet.\"\n />\n )}\n </TabsContent>\n\n <TabsContent value=\"availability\" className=\"mt-4\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Availability</CardTitle>\n <CardDescription>Real-time scheduling with this provider</CardDescription>\n </CardHeader>\n <CardContent>\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title=\"Scheduling is coming soon\"\n description=\"Live availability and consultation booking will appear here once the scheduling backend ships. In the meantime, send a message to arrange a time.\"\n action={\n <Button size=\"sm\" onClick={() => navigate('../../marketplace/messaging-page')}>\n <MessageSquare className=\"h-3 w-3 mr-1\" /> Message provider\n </Button>\n }\n />\n </CardContent>\n </Card>\n </TabsContent>\n\n <TabsContent value=\"analytics\" className=\"mt-4\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Service Performance</CardTitle>\n <CardDescription>Ratings and reviews per listed service</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {providerServices.length > 0 ? (\n providerServices.map((service) => (\n <div key={service.id} className=\"flex items-center justify-between\">\n <div className=\"min-w-0\">\n <p className=\"text-sm font-medium truncate\">{service.title}</p>\n <p className=\"text-xs text-text-secondary\">{service.category}</p>\n </div>\n <div className=\"flex items-center gap-2 text-sm shrink-0\">\n <Star className=\"h-3.5 w-3.5 text-status-warning-text\" />\n <span className=\"font-semibold\">{service.rating}</span>\n <span className=\"text-xs text-text-secondary\">({service.reviews})</span>\n </div>\n </div>\n ))\n ) : (\n <p className=\"text-sm text-text-secondary\">No performance data yet.</p>\n )}\n </CardContent>\n </Card>\n </TabsContent>\n </Tabs>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MarketplaceFreelancerProfilePagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/MarketplaceFreelancerProfilePagePage.tsx"],"sourcesContent":["import {\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n EmphasisPanel,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { Award, Briefcase, DollarSign, MessageSquare, Star } from 'lucide-react';\nimport { useMemo } from 'react';\nimport { useLocation, useNavigate } from 'react-router-dom';\nimport { useCrewFoundryStore } from '../store/crewfoundryStore';\n\nconst initialsOf = (name: string): string =>\n name\n .split(' ')\n .map((part) => part[0] ?? '')\n .join('')\n .slice(0, 2)\n .toUpperCase() || '—';\n\nexport function MarketplaceFreelancerProfilePagePage() {\n const navigate = useNavigate();\n const location = useLocation();\n const { marketplaceServices } = useCrewFoundryStore();\n const selectedId = new URLSearchParams(location.search).get('id');\n\n const baseService =\n marketplaceServices.find((service) => service.id === selectedId) ?? marketplaceServices[0];\n const providerName = baseService?.provider ?? '';\n\n const providerServices = useMemo(\n () => marketplaceServices.filter((service) => service.provider === providerName),\n [marketplaceServices, providerName]\n );\n\n const skills = useMemo(\n () => Array.from(new Set(providerServices.flatMap((service) => service.skills))).slice(0, 20),\n [providerServices]\n );\n\n const categories = useMemo(\n () => Array.from(new Set(providerServices.map((service) => service.category))),\n [providerServices]\n );\n\n const avgRating =\n providerServices.length > 0\n ? (\n providerServices.reduce((sum, service) => sum + service.rating, 0) /\n providerServices.length\n ).toFixed(1)\n : '0.0';\n const totalReviews = providerServices.reduce((sum, service) => sum + service.reviews, 0);\n const startingRate =\n providerServices.length > 0\n ? Math.min(...providerServices.map((service) => service.price))\n : 0;\n const isTopRated = Number(avgRating) >= 4.5 && totalReviews > 0;\n\n if (!baseService) {\n return (\n <div className=\"p-6\">\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"Provider not found\"\n description=\"This freelancer is no longer available on the marketplace.\"\n />\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-start gap-6\">\n <div className=\"flex h-20 w-20 shrink-0 items-center justify-center rounded-full bg-action-primary-bg text-action-primary-text text-2xl font-semibold\">\n {initialsOf(providerName)}\n </div>\n <div className=\"flex-1 space-y-2\">\n <div className=\"flex items-center gap-3\">\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>{providerName}</GradientText>\n </h1>\n {isTopRated ? <Badge>Top Rated</Badge> : null}\n </div>\n <p className=\"text-sm text-text-secondary\">\n {categories.length > 0\n ? `Provider · ${categories.join(', ')}`\n : 'Marketplace provider'}\n </p>\n <PagePurpose className=\"mt-1\">Review a freelancer's full profile — skills, listed services, and ratings — to decide if they're the right fit before you hire.</PagePurpose>\n <div className=\"flex flex-wrap items-center gap-4 text-xs text-text-secondary\">\n <span className=\"flex items-center gap-1\">\n <Star className=\"h-3 w-3 text-status-warning-text\" /> {avgRating} ({totalReviews}{' '}\n reviews)\n </span>\n <span className=\"flex items-center gap-1\">\n <Briefcase className=\"h-3 w-3\" /> {providerServices.length} services\n </span>\n </div>\n <div className=\"flex gap-2 pt-1\">\n <Button size=\"sm\" onClick={() => navigate('../../marketplace/messaging-page')}>\n <MessageSquare className=\"h-3 w-3 mr-1\" /> Message\n </Button>\n {/* No consultation-booking mutation exists yet — honest disabled control. */}\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled\n title=\"Consultation booking is coming soon\"\n aria-label=\"Book consultation — coming soon\"\n >\n Book Consultation · Coming soon\n </Button>\n </div>\n </div>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-4\">\n {[\n {\n icon: Briefcase,\n label: 'Services',\n value: `${providerServices.length}`,\n color: 'text-status-info-text',\n },\n {\n icon: Star,\n label: 'Avg Rating',\n value: `${avgRating}`,\n color: 'text-status-warning-text',\n },\n {\n icon: Award,\n label: 'Total Reviews',\n value: `${totalReviews}`,\n color: 'text-status-success-text',\n },\n {\n icon: DollarSign,\n label: 'Starting Rate',\n value: `$${startingRate.toLocaleString()}`,\n color: 'text-action-primary-bg',\n },\n ].map((stat) => (\n <Card key={stat.label}>\n <CardContent className=\"flex items-center gap-3 pt-6\">\n <stat.icon className={`h-5 w-5 ${stat.color}`} />\n <div>\n <p className=\"text-xs text-text-secondary\">{stat.label}</p>\n <p className=\"text-lg font-bold\">{stat.value}</p>\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n\n <div className=\"grid gap-6 lg:grid-cols-3\">\n <div className=\"lg:col-span-2 space-y-6\">\n <EmphasisPanel>\n <div className=\"mb-2 text-base font-semibold\">Overview</div>\n <p className=\"text-sm text-text-secondary\">\n {providerName} offers {providerServices.length}{' '}\n {providerServices.length === 1 ? 'service' : 'services'} on the marketplace\n {categories.length > 0 ? ` across ${categories.join(', ')}` : ''}, with an average\n rating of {avgRating} from {totalReviews} reviews.\n </p>\n </EmphasisPanel>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Listed Services</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {providerServices.map((service) => (\n <button\n key={service.id}\n type=\"button\"\n onClick={() =>\n navigate(`../../marketplace/service-details-page?id=${service.id}`)\n }\n className=\"w-full rounded-lg border p-4 space-y-2 text-left hover:bg-bg-sunken/50 focus:outline-none focus-visible:ring-2 focus-visible:ring-action-primary-bg\"\n >\n <div className=\"flex items-center justify-between\">\n <p className=\"font-medium text-sm\">{service.title}</p>\n <Badge variant=\"secondary\" className=\"text-[10px]\">\n {service.deliveryTime}\n </Badge>\n </div>\n <p className=\"text-xs text-text-secondary\">{service.description}</p>\n <div className=\"flex items-center gap-3 text-xs\">\n <span className=\"font-medium\">\n ${service.price.toLocaleString()}/{service.priceType}\n </span>\n <span className=\"flex items-center gap-1\">\n <Star className=\"h-3 w-3 text-status-warning-text\" />\n {service.rating} ({service.reviews})\n </span>\n </div>\n </button>\n ))}\n </CardContent>\n </Card>\n </div>\n\n <div className=\"space-y-4\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-sm\">Skills & Expertise</CardTitle>\n </CardHeader>\n <CardContent className=\"flex flex-wrap gap-1.5\">\n {skills.length > 0 ? (\n skills.map((skill) => (\n <Badge key={skill} variant=\"secondary\" className=\"text-xs\">\n {skill}\n </Badge>\n ))\n ) : (\n <p className=\"text-xs text-text-secondary\">No skills listed yet.</p>\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"text-sm\">Service Categories</CardTitle>\n </CardHeader>\n <CardContent className=\"flex flex-wrap gap-1.5\">\n {categories.length > 0 ? (\n categories.map((category) => (\n <Badge key={category} variant=\"outline\" className=\"text-xs\">\n {category}\n </Badge>\n ))\n ) : (\n <p className=\"text-xs text-text-secondary\">No categories listed yet.</p>\n )}\n </CardContent>\n </Card>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;AAiBA,IAAM,KAAc,MAClB,EACG,MAAM,IAAI,CACV,KAAK,MAAS,EAAK,MAAM,GAAG,CAC5B,KAAK,GAAG,CACR,MAAM,GAAG,EAAE,CACX,aAAa,IAAI;AAEtB,SAAgB,IAAuC;CACrD,IAAM,IAAW,GAAa,EACxB,IAAW,GAAa,EACxB,EAAE,2BAAwB,GAAqB,EAC/C,IAAa,IAAI,gBAAgB,EAAS,OAAO,CAAC,IAAI,KAAK,EAE3D,IACJ,EAAoB,MAAM,MAAY,EAAQ,OAAO,EAAW,IAAI,EAAoB,IACpF,IAAe,GAAa,YAAY,IAExC,IAAmB,QACjB,EAAoB,QAAQ,MAAY,EAAQ,aAAa,EAAa,EAChF,CAAC,GAAqB,EAAa,CACpC,EAEK,IAAS,QACP,MAAM,KAAK,IAAI,IAAI,EAAiB,SAAS,MAAY,EAAQ,OAAO,CAAC,CAAC,CAAC,MAAM,GAAG,GAAG,EAC7F,CAAC,EAAiB,CACnB,EAEK,IAAa,QACX,MAAM,KAAK,IAAI,IAAI,EAAiB,KAAK,MAAY,EAAQ,SAAS,CAAC,CAAC,EAC9E,CAAC,EAAiB,CACnB,EAEK,IACJ,EAAiB,SAAS,KAEpB,EAAiB,QAAQ,GAAK,MAAY,IAAM,EAAQ,QAAQ,EAAE,GAClE,EAAiB,QACjB,QAAQ,EAAE,GACZ,OACA,IAAe,EAAiB,QAAQ,GAAK,MAAY,IAAM,EAAQ,SAAS,EAAE,EAClF,IACJ,EAAiB,SAAS,IACtB,KAAK,IAAI,GAAG,EAAiB,KAAK,MAAY,EAAQ,MAAM,CAAC,GAC7D,GACA,IAAa,OAAO,EAAU,IAAI,OAAO,IAAe;AAc9D,QAZK,IAaH,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAW,EAAa;KACrB,CAAA,EACN,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,MAAD;QAAI,WAAU;kBACZ,kBAAC,GAAD,EAAA,UAAe,GAA4B,CAAA;QACxC,CAAA,EACJ,IAAa,kBAAC,GAAD,EAAA,UAAO,aAAiB,CAAA,GAAG,KACrC;;MACN,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAW,SAAS,IACjB,cAAc,EAAW,KAAK,KAAK,KACnC;OACF,CAAA;MACJ,kBAAC,GAAD;OAAa,WAAU;iBAAO;OAAuJ,CAAA;MACrL,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,QAAD;QAAM,WAAU;kBAAhB;SACE,kBAAC,GAAD,EAAM,WAAU,oCAAqC,CAAA;;SAAE;SAAU;SAAG;SAAc;SAAI;SAEjF;WACP,kBAAC,QAAD;QAAM,WAAU;kBAAhB;SACE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA;;SAAE,EAAiB;SAAO;SACtD;UACH;;MACN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QAAQ,MAAK;QAAK,eAAe,EAAS,mCAAmC;kBAA7E,CACE,kBAAC,GAAD,EAAe,WAAU,gBAAiB,CAAA,EAAA,WACnC;WAET,kBAAC,GAAD;QACE,MAAK;QACL,SAAQ;QACR,UAAA;QACA,OAAM;QACN,cAAW;kBACZ;QAEQ,CAAA,CACL;;MACF;OACF;;GAEN,kBAAC,OAAD;IAAK,WAAU;cACZ;KACC;MACE,MAAM;MACN,OAAO;MACP,OAAO,GAAG,EAAiB;MAC3B,OAAO;MACR;KACD;MACE,MAAM;MACN,OAAO;MACP,OAAO,GAAG;MACV,OAAO;MACR;KACD;MACE,MAAM;MACN,OAAO;MACP,OAAO,GAAG;MACV,OAAO;MACR;KACD;MACE,MAAM;MACN,OAAO;MACP,OAAO,IAAI,EAAa,gBAAgB;MACxC,OAAO;MACR;KACF,CAAC,KAAK,MACL,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;KAAa,WAAU;eAAvB,CACE,kBAAC,EAAK,MAAN,EAAW,WAAW,WAAW,EAAK,SAAW,CAAA,EACjD,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;MAAG,WAAU;gBAA+B,EAAK;MAAU,CAAA,EAC3D,kBAAC,KAAD;MAAG,WAAU;gBAAqB,EAAK;MAAU,CAAA,CAC7C,EAAA,CAAA,CACM;QACT,EARI,EAAK,MAQT,CACP;IACE,CAAA;GAEN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,OAAD;MAAK,WAAU;gBAA+B;MAAc,CAAA,EAC5D,kBAAC,KAAD;MAAG,WAAU;gBAAb;OACG;OAAa;OAAS,EAAiB;OAAQ;OAC/C,EAAiB,WAAW,IAAI,YAAY;OAAW;OACvD,EAAW,SAAS,IAAI,WAAW,EAAW,KAAK,KAAK,KAAK;OAAG;OACtD;OAAU;OAAO;OAAa;OACvC;QACU,EAAA,CAAA,EAEhB,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;MAAW,WAAU;gBAAY;MAA2B,CAAA,EACjD,CAAA,EACb,kBAAC,GAAD;MAAa,WAAU;gBACpB,EAAiB,KAAK,MACrB,kBAAC,UAAD;OAEE,MAAK;OACL,eACE,EAAS,6CAA6C,EAAQ,KAAK;OAErE,WAAU;iBANZ;QAQE,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,KAAD;UAAG,WAAU;oBAAuB,EAAQ;UAAU,CAAA,EACtD,kBAAC,GAAD;UAAO,SAAQ;UAAY,WAAU;oBAClC,EAAQ;UACH,CAAA,CACJ;;QACN,kBAAC,KAAD;SAAG,WAAU;mBAA+B,EAAQ;SAAgB,CAAA;QACpE,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,QAAD;UAAM,WAAU;oBAAhB;WAA8B;WAC1B,EAAQ,MAAM,gBAAgB;WAAC;WAAE,EAAQ;WACtC;aACP,kBAAC,QAAD;UAAM,WAAU;oBAAhB;WACE,kBAAC,GAAD,EAAM,WAAU,oCAAqC,CAAA;WACpD,EAAQ;WAAO;WAAG,EAAQ;WAAQ;WAC9B;YACH;;QACC;SAvBF,EAAQ,GAuBN,CACT;MACU,CAAA,CACT,EAAA,CAAA,CACH;QAEN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;MAAW,WAAU;gBAAU;MAAkC,CAAA,EACtD,CAAA,EACb,kBAAC,GAAD;MAAa,WAAU;gBACpB,EAAO,SAAS,IACf,EAAO,KAAK,MACV,kBAAC,GAAD;OAAmB,SAAQ;OAAY,WAAU;iBAC9C;OACK,EAFI,EAEJ,CACR,GAEF,kBAAC,KAAD;OAAG,WAAU;iBAA8B;OAAyB,CAAA;MAE1D,CAAA,CACT,EAAA,CAAA,EAEP,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;MAAW,WAAU;gBAAU;MAA8B,CAAA,EAClD,CAAA,EACb,kBAAC,GAAD;MAAa,WAAU;gBACpB,EAAW,SAAS,IACnB,EAAW,KAAK,MACd,kBAAC,GAAD;OAAsB,SAAQ;OAAU,WAAU;iBAC/C;OACK,EAFI,EAEJ,CACR,GAEF,kBAAC,KAAD;OAAG,WAAU;iBAA8B;OAA6B,CAAA;MAE9D,CAAA,CACT,EAAA,CAAA,CACH;OACF;;GACF;MAnLJ,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD;GACE,cAAa;GACb,OAAM;GACN,aAAY;GACZ,CAAA;EACE,CAAA"}
|
|
1
|
+
{"version":3,"file":"MarketplaceFreelancerProfilePagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/MarketplaceFreelancerProfilePagePage.tsx"],"sourcesContent":["import {\n Badge,\n Button,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n EmphasisPanel,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { Award, Briefcase, DollarSign, MessageSquare, Star } from 'lucide-react';\nimport { useMemo } from 'react';\nimport { useLocation, useNavigate } from 'react-router-dom';\nimport { useCrewFoundryStore } from '../store/crewfoundryStore';\n\nconst initialsOf = (name: string): string =>\n name\n .split(' ')\n .map((part) => part[0] ?? '')\n .join('')\n .slice(0, 2)\n .toUpperCase() || '—';\n\nexport function MarketplaceFreelancerProfilePagePage() {\n const navigate = useNavigate();\n const location = useLocation();\n const { marketplaceServices } = useCrewFoundryStore();\n const selectedId = new URLSearchParams(location.search).get('id');\n\n const baseService =\n marketplaceServices.find((service) => service.id === selectedId) ?? marketplaceServices[0];\n const providerName = baseService?.provider ?? '';\n\n const providerServices = useMemo(\n () => marketplaceServices.filter((service) => service.provider === providerName),\n [marketplaceServices, providerName]\n );\n\n const skills = useMemo(\n () => Array.from(new Set(providerServices.flatMap((service) => service.skills))).slice(0, 20),\n [providerServices]\n );\n\n const categories = useMemo(\n () => Array.from(new Set(providerServices.map((service) => service.category))),\n [providerServices]\n );\n\n const avgRating =\n providerServices.length > 0\n ? (\n providerServices.reduce((sum, service) => sum + service.rating, 0) /\n providerServices.length\n ).toFixed(1)\n : '0.0';\n const totalReviews = providerServices.reduce((sum, service) => sum + service.reviews, 0);\n const startingRate =\n providerServices.length > 0 ? Math.min(...providerServices.map((service) => service.price)) : 0;\n const isTopRated = Number(avgRating) >= 4.5 && totalReviews > 0;\n\n if (!baseService) {\n return (\n <div className=\"p-6\">\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"Provider not found\"\n description=\"This freelancer is no longer available on the marketplace.\"\n />\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-start gap-6\">\n <div className=\"flex h-20 w-20 shrink-0 items-center justify-center rounded-full bg-action-primary-bg text-action-primary-text text-2xl font-semibold\">\n {initialsOf(providerName)}\n </div>\n <div className=\"flex-1 space-y-2\">\n <div className=\"flex items-center gap-3\">\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>{providerName}</GradientText>\n </h1>\n {isTopRated ? <Badge>Top Rated</Badge> : null}\n </div>\n <p className=\"text-sm text-text-secondary\">\n {categories.length > 0 ? `Provider · ${categories.join(', ')}` : 'Marketplace provider'}\n </p>\n <PagePurpose className=\"mt-1\">\n Review a freelancer's full profile — skills, listed services, and ratings — to\n decide if they're the right fit before you hire.\n </PagePurpose>\n <div className=\"flex flex-wrap items-center gap-4 text-xs text-text-secondary\">\n <span className=\"flex items-center gap-1\">\n <Star className=\"h-3 w-3 text-status-warning-text\" /> {avgRating} ({totalReviews}{' '}\n reviews)\n </span>\n <span className=\"flex items-center gap-1\">\n <Briefcase className=\"h-3 w-3\" /> {providerServices.length} services\n </span>\n </div>\n <div className=\"flex gap-2 pt-1\">\n <Button size=\"sm\" onClick={() => navigate('../../marketplace/messaging-page')}>\n <MessageSquare className=\"h-3 w-3 mr-1\" /> Message\n </Button>\n {/* No consultation-booking mutation exists yet — honest disabled control. */}\n <Button\n size=\"sm\"\n variant=\"outline\"\n disabled\n title=\"Consultation booking is coming soon\"\n aria-label=\"Book consultation — coming soon\"\n >\n Book Consultation · Coming soon\n </Button>\n </div>\n </div>\n </div>\n\n <div className=\"grid gap-4 md:grid-cols-4\">\n {[\n {\n icon: Briefcase,\n label: 'Services',\n value: `${providerServices.length}`,\n color: 'text-status-info-text',\n },\n {\n icon: Star,\n label: 'Avg Rating',\n value: `${avgRating}`,\n color: 'text-status-warning-text',\n },\n {\n icon: Award,\n label: 'Total Reviews',\n value: `${totalReviews}`,\n color: 'text-status-success-text',\n },\n {\n icon: DollarSign,\n label: 'Starting Rate',\n value: `$${startingRate.toLocaleString()}`,\n color: 'text-action-primary-bg',\n },\n ].map((stat) => (\n <Card key={stat.label}>\n <CardContent className=\"flex items-center gap-3 pt-6\">\n <stat.icon className={`h-5 w-5 ${stat.color}`} />\n <div>\n <p className=\"text-xs text-text-secondary\">{stat.label}</p>\n <p className=\"text-lg font-bold\">{stat.value}</p>\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n\n <div className=\"grid gap-6 lg:grid-cols-3\">\n <div className=\"lg:col-span-2 space-y-6\">\n <EmphasisPanel>\n <div className=\"mb-2 text-base font-semibold\">Overview</div>\n <p className=\"text-sm text-text-secondary\">\n {providerName} offers {providerServices.length}{' '}\n {providerServices.length === 1 ? 'service' : 'services'} on the marketplace\n {categories.length > 0 ? ` across ${categories.join(', ')}` : ''}, with an average\n rating of {avgRating} from {totalReviews} reviews.\n </p>\n </EmphasisPanel>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Listed Services</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {providerServices.map((service) => (\n <button\n key={service.id}\n type=\"button\"\n onClick={() =>\n navigate(`../../marketplace/service-details-page?id=${service.id}`)\n }\n className=\"w-full rounded-lg border p-4 space-y-2 text-left hover:bg-bg-sunken/50 focus:outline-none focus-visible:ring-2 focus-visible:ring-action-primary-bg\"\n >\n <div className=\"flex items-center justify-between\">\n <p className=\"font-medium text-sm\">{service.title}</p>\n <Badge variant=\"secondary\" className=\"text-[10px]\">\n {service.deliveryTime}\n </Badge>\n </div>\n <p className=\"text-xs text-text-secondary\">{service.description}</p>\n <div className=\"flex items-center gap-3 text-xs\">\n <span className=\"font-medium\">\n ${service.price.toLocaleString()}/{service.priceType}\n </span>\n <span className=\"flex items-center gap-1\">\n <Star className=\"h-3 w-3 text-status-warning-text\" />\n {service.rating} ({service.reviews})\n </span>\n </div>\n </button>\n ))}\n </CardContent>\n </Card>\n </div>\n\n <div className=\"space-y-4\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-sm\">Skills & Expertise</CardTitle>\n </CardHeader>\n <CardContent className=\"flex flex-wrap gap-1.5\">\n {skills.length > 0 ? (\n skills.map((skill) => (\n <Badge key={skill} variant=\"secondary\" className=\"text-xs\">\n {skill}\n </Badge>\n ))\n ) : (\n <p className=\"text-xs text-text-secondary\">No skills listed yet.</p>\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"text-sm\">Service Categories</CardTitle>\n </CardHeader>\n <CardContent className=\"flex flex-wrap gap-1.5\">\n {categories.length > 0 ? (\n categories.map((category) => (\n <Badge key={category} variant=\"outline\" className=\"text-xs\">\n {category}\n </Badge>\n ))\n ) : (\n <p className=\"text-xs text-text-secondary\">No categories listed yet.</p>\n )}\n </CardContent>\n </Card>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MarketplaceMarketplacePagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/MarketplaceMarketplacePagePage.tsx"],"sourcesContent":["import {\n Badge,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n PagePurpose,\n Pagination,\n} from '@burdenoff/fe-libs/ui';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { usePagination } from '@burdenoff/fe-libs/shared/hooks';\nimport { ShoppingBag, Star, Users } from 'lucide-react';\nimport { useNavigate } from 'react-router-dom';\nimport { useCrewFoundryStore } from '../store/crewfoundryStore';\n\nexport function MarketplaceMarketplacePagePage() {\n const { t: translate } = useI18n();\n const navigate = useNavigate();\n const { marketplaceServices } = useCrewFoundryStore();\n const t = (key: string, fallback: string) => {\n const value = translate(key);\n return value === key ? fallback : value;\n };\n const { page, pageSize, offset, setPage } = usePagination({ initialPageSize: 9 });\n const avgRating =\n marketplaceServices.length > 0\n ? (\n marketplaceServices.reduce((s, m) => s + m.rating, 0) / marketplaceServices.length\n ).toFixed(1)\n : '0.0';\n const pagedServices = marketplaceServices.slice(offset, offset + pageSize);\n const openService = (id: string) => navigate(`../../marketplace/service-details-page?id=${id}`);\n\n return (\n <div className=\"space-y-6 p-6\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>\n {t('crewfoundry.marketplace.title', 'HR Services Marketplace')}\n </GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {t('crewfoundry.marketplace.subtitle', 'Find and hire professional HR service providers')}\n </p>\n <PagePurpose className=\"mt-1\"
|
|
1
|
+
{"version":3,"file":"MarketplaceMarketplacePagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/MarketplaceMarketplacePagePage.tsx"],"sourcesContent":["import {\n Badge,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n PagePurpose,\n Pagination,\n} from '@burdenoff/fe-libs/ui';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { usePagination } from '@burdenoff/fe-libs/shared/hooks';\nimport { ShoppingBag, Star, Users } from 'lucide-react';\nimport { useNavigate } from 'react-router-dom';\nimport { useCrewFoundryStore } from '../store/crewfoundryStore';\n\nexport function MarketplaceMarketplacePagePage() {\n const { t: translate } = useI18n();\n const navigate = useNavigate();\n const { marketplaceServices } = useCrewFoundryStore();\n const t = (key: string, fallback: string) => {\n const value = translate(key);\n return value === key ? fallback : value;\n };\n const { page, pageSize, offset, setPage } = usePagination({ initialPageSize: 9 });\n const avgRating =\n marketplaceServices.length > 0\n ? (\n marketplaceServices.reduce((s, m) => s + m.rating, 0) / marketplaceServices.length\n ).toFixed(1)\n : '0.0';\n const pagedServices = marketplaceServices.slice(offset, offset + pageSize);\n const openService = (id: string) => navigate(`../../marketplace/service-details-page?id=${id}`);\n\n return (\n <div className=\"space-y-6 p-6\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>\n {t('crewfoundry.marketplace.title', 'HR Services Marketplace')}\n </GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {t('crewfoundry.marketplace.subtitle', 'Find and hire professional HR service providers')}\n </p>\n <PagePurpose className=\"mt-1\">\n Discover freelancers and services for your projects — browse the marketplace to find and\n hire the right talent on demand.\n </PagePurpose>\n </div>\n <div className=\"grid gap-4 md:grid-cols-3\">\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">\n {t('crewfoundry.marketplace.stats.services', 'Services')}\n </CardTitle>\n <ShoppingBag className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{marketplaceServices.length}</div>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">\n {t('crewfoundry.marketplace.stats.avgRating', 'Avg Rating')}\n </CardTitle>\n <Star className=\"h-4 w-4 text-status-warning-text\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{avgRating}</div>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">\n {t('crewfoundry.marketplace.stats.totalReviews', 'Total Reviews')}\n </CardTitle>\n <Users className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">\n {marketplaceServices.reduce((s, m) => s + m.reviews, 0)}\n </div>\n </CardContent>\n </GlassCard>\n </div>\n <h2 className=\"text-lg font-medium\">\n {t('crewfoundry.marketplace.featured.title', 'Featured Services')}\n </h2>\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-3\">\n {pagedServices.map((svc) => (\n <Card\n key={svc.id}\n role=\"button\"\n tabIndex={0}\n aria-label={`View details for ${svc.title}`}\n onClick={() => openService(svc.id)}\n onKeyDown={(event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n openService(svc.id);\n }\n }}\n className=\"hover:shadow-md transition-shadow cursor-pointer focus:outline-none focus-visible:ring-2 focus-visible:ring-action-primary-bg\"\n >\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <Badge variant=\"outline\" className=\"text-[10px]\">\n {svc.category}\n </Badge>\n <Badge\n variant={svc.status === 'active' ? 'default' : 'secondary'}\n className=\"text-[10px]\"\n >\n {svc.status}\n </Badge>\n </div>\n <CardTitle className=\"text-base mt-2\">{svc.title}</CardTitle>\n <CardDescription>{svc.provider}</CardDescription>\n </CardHeader>\n <CardContent>\n <p className=\"text-sm text-text-secondary mb-3\">{svc.description}</p>\n <div className=\"flex flex-wrap gap-1 mb-3\">\n {svc.skills.map((s) => (\n <Badge key={s} variant=\"outline\" className=\"text-[9px]\">\n {s}\n </Badge>\n ))}\n </div>\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"font-bold\">\n ${svc.price.toLocaleString()}\n <span className=\"text-xs font-normal text-text-secondary\">/{svc.priceType}</span>\n </span>\n <span className=\"flex items-center gap-1\">\n <Star className=\"h-3 w-3 text-status-warning-text\" />\n {svc.rating} ({svc.reviews})\n </span>\n </div>\n <p className=\"text-xs text-text-secondary mt-1\">\n {t('crewfoundry.marketplace.card.delivery', 'Delivery')}: {svc.deliveryTime}\n </p>\n </CardContent>\n </Card>\n ))}\n </div>\n <Pagination\n page={page}\n pageSize={pageSize}\n totalItems={marketplaceServices.length}\n onPageChange={setPage}\n />\n </div>\n );\n}\n"],"mappings":";;;;;;;;AAkBA,SAAgB,IAAiC;CAC/C,IAAM,EAAE,GAAG,MAAc,GAAS,EAC5B,IAAW,GAAa,EACxB,EAAE,2BAAwB,GAAqB,EAC/C,KAAK,GAAa,MAAqB;EAC3C,IAAM,IAAQ,EAAU,EAAI;AAC5B,SAAO,MAAU,IAAM,IAAW;IAE9B,EAAE,SAAM,aAAU,WAAQ,eAAY,EAAc,EAAE,iBAAiB,GAAG,CAAC,EAC3E,IACJ,EAAoB,SAAS,KAEvB,EAAoB,QAAQ,GAAG,MAAM,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAoB,QAC5E,QAAQ,EAAE,GACZ,OACA,IAAgB,EAAoB,MAAM,GAAQ,IAAS,EAAS,EACpE,KAAe,MAAe,EAAS,6CAA6C,IAAK;AAE/F,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD,EAAA,UAAA;IACE,kBAAC,MAAD;KAAI,WAAU;eACZ,kBAAC,GAAD,EAAA,UACG,EAAE,iCAAiC,0BAA0B,EACjD,CAAA;KACZ,CAAA;IACL,kBAAC,KAAD;KAAG,WAAU;eACV,EAAE,oCAAoC,kDAAkD;KACvF,CAAA;IACJ,kBAAC,GAAD;KAAa,WAAU;eAAO;KAGhB,CAAA;IACV,EAAA,CAAA;GACN,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD;MAAW,WAAU;MAAQ,MAAA;MAAK,WAAU;gBAA5C,CACE,kBAAC,GAAD;OAAY,WAAU;iBAAtB,CACE,kBAAC,GAAD;QAAW,WAAU;kBAClB,EAAE,0CAA0C,WAAW;QAC9C,CAAA,EACZ,kBAAC,GAAD,EAAa,WAAU,+BAAgC,CAAA,CAC5C;UACb,kBAAC,GAAD,EAAA,UACE,kBAAC,OAAD;OAAK,WAAU;iBAAmC,EAAoB;OAAa,CAAA,EACvE,CAAA,CACJ;;KACZ,kBAAC,GAAD;MAAW,WAAU;MAAS,WAAU;gBAAxC,CACE,kBAAC,GAAD;OAAY,WAAU;iBAAtB,CACE,kBAAC,GAAD;QAAW,WAAU;kBAClB,EAAE,2CAA2C,aAAa;QACjD,CAAA,EACZ,kBAAC,GAAD,EAAM,WAAU,oCAAqC,CAAA,CAC1C;UACb,kBAAC,GAAD,EAAA,UACE,kBAAC,OAAD;OAAK,WAAU;iBAAmC;OAAgB,CAAA,EACtD,CAAA,CACJ;;KACZ,kBAAC,GAAD;MAAW,WAAU;MAAS,WAAU;gBAAxC,CACE,kBAAC,GAAD;OAAY,WAAU;iBAAtB,CACE,kBAAC,GAAD;QAAW,WAAU;kBAClB,EAAE,8CAA8C,gBAAgB;QACvD,CAAA,EACZ,kBAAC,GAAD,EAAO,WAAU,+BAAgC,CAAA,CACtC;UACb,kBAAC,GAAD,EAAA,UACE,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAoB,QAAQ,GAAG,MAAM,IAAI,EAAE,SAAS,EAAE;OACnD,CAAA,EACM,CAAA,CACJ;;KACR;;GACN,kBAAC,MAAD;IAAI,WAAU;cACX,EAAE,0CAA0C,oBAAoB;IAC9D,CAAA;GACL,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAc,KAAK,MAClB,kBAAC,GAAD;KAEE,MAAK;KACL,UAAU;KACV,cAAY,oBAAoB,EAAI;KACpC,eAAe,EAAY,EAAI,GAAG;KAClC,YAAY,MAAU;AACpB,OAAI,EAAM,QAAQ,WAAW,EAAM,QAAQ,SACzC,EAAM,gBAAgB,EACtB,EAAY,EAAI,GAAG;;KAGvB,WAAU;eAZZ,CAcE,kBAAC,GAAD,EAAA,UAAA;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QAAO,SAAQ;QAAU,WAAU;kBAChC,EAAI;QACC,CAAA,EACR,kBAAC,GAAD;QACE,SAAS,EAAI,WAAW,WAAW,YAAY;QAC/C,WAAU;kBAET,EAAI;QACC,CAAA,CACJ;;MACN,kBAAC,GAAD;OAAW,WAAU;iBAAkB,EAAI;OAAkB,CAAA;MAC7D,kBAAC,GAAD,EAAA,UAAkB,EAAI,UAA2B,CAAA;MACtC,EAAA,CAAA,EACb,kBAAC,GAAD,EAAA,UAAA;MACE,kBAAC,KAAD;OAAG,WAAU;iBAAoC,EAAI;OAAgB,CAAA;MACrE,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAI,OAAO,KAAK,MACf,kBAAC,GAAD;QAAe,SAAQ;QAAU,WAAU;kBACxC;QACK,EAFI,EAEJ,CACR;OACE,CAAA;MACN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,QAAD;QAAM,WAAU;kBAAhB;SAA4B;SACxB,EAAI,MAAM,gBAAgB;SAC5B,kBAAC,QAAD;UAAM,WAAU;oBAAhB,CAA0D,KAAE,EAAI,UAAiB;;SAC5E;WACP,kBAAC,QAAD;QAAM,WAAU;kBAAhB;SACE,kBAAC,GAAD,EAAM,WAAU,oCAAqC,CAAA;SACpD,EAAI;SAAO;SAAG,EAAI;SAAQ;SACtB;UACH;;MACN,kBAAC,KAAD;OAAG,WAAU;iBAAb;QACG,EAAE,yCAAyC,WAAW;QAAC;QAAG,EAAI;QAC7D;;MACQ,EAAA,CAAA,CACT;OAnDA,EAAI,GAmDJ,CACP;IACE,CAAA;GACN,kBAAC,GAAD;IACQ;IACI;IACV,YAAY,EAAoB;IAChC,cAAc;IACd,CAAA;GACE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MarketplaceMarketplaceServicesPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/MarketplaceMarketplaceServicesPagePage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport {\n Badge,\n CardContent,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n Input,\n PagePurpose,\n Pagination,\n ResponsiveTable,\n type ResponsiveTableColumn,\n} from '@burdenoff/fe-libs/ui';\nimport { usePagination } from '@burdenoff/fe-libs/shared/hooks';\nimport { Search, Star } from 'lucide-react';\nimport { useNavigate } from 'react-router-dom';\nimport { useCrewFoundryStore, type MarketplaceService } from '../store/crewfoundryStore';\n\nexport function MarketplaceMarketplaceServicesPagePage() {\n const navigate = useNavigate();\n const { marketplaceServices } = useCrewFoundryStore();\n const [search, setSearch] = useState('');\n const { page, pageSize, offset, setPage } = usePagination({ initialPageSize: 10 });\n const filtered = marketplaceServices.filter((s) =>\n `${s.title} ${s.provider} ${s.category}`.toLowerCase().includes(search.toLowerCase())\n );\n const paged = filtered.slice(offset, offset + pageSize);\n\n const columns: ResponsiveTableColumn<MarketplaceService>[] = [\n {\n key: 'service',\n header: 'Service',\n priority: 'primary',\n cell: (svc) => (\n <div>\n <p className=\"font-medium\">{svc.title}</p>\n <p className=\"text-xs text-text-secondary truncate\">\n {svc.description.slice(0, 60)}\n {svc.description.length > 60 ? '…' : ''}\n </p>\n </div>\n ),\n },\n {\n key: 'provider',\n header: 'Provider',\n priority: 'secondary',\n cell: (svc) => <span className=\"text-text-secondary\">{svc.provider}</span>,\n },\n {\n key: 'category',\n header: 'Category',\n priority: 'secondary',\n cell: (svc) => (\n <Badge variant=\"outline\" className=\"w-fit h-fit text-[10px]\">\n {svc.category}\n </Badge>\n ),\n },\n {\n key: 'price',\n header: 'Price',\n priority: 'secondary',\n cell: (svc) => (\n <span className=\"font-medium\">\n ${svc.price.toLocaleString()}/{svc.priceType}\n </span>\n ),\n },\n {\n key: 'rating',\n header: 'Rating',\n priority: 'secondary',\n cell: (svc) => (\n <span className=\"flex items-center gap-1\">\n <Star className=\"h-3 w-3 text-status-warning-text\" />\n {svc.rating} ({svc.reviews})\n </span>\n ),\n },\n {\n key: 'status',\n header: 'Status',\n priority: 'secondary',\n cell: (svc) => (\n <Badge variant={svc.status === 'active' ? 'default' : 'secondary'} className=\"text-[10px]\">\n {svc.status}\n </Badge>\n ),\n },\n ];\n\n return (\n <div className=\"space-y-6 p-6\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>Browse Services</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {marketplaceServices.length} services available\n </p>\n <PagePurpose className=\"mt-1\"
|
|
1
|
+
{"version":3,"file":"MarketplaceMarketplaceServicesPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/MarketplaceMarketplaceServicesPagePage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport {\n Badge,\n CardContent,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n Input,\n PagePurpose,\n Pagination,\n ResponsiveTable,\n type ResponsiveTableColumn,\n} from '@burdenoff/fe-libs/ui';\nimport { usePagination } from '@burdenoff/fe-libs/shared/hooks';\nimport { Search, Star } from 'lucide-react';\nimport { useNavigate } from 'react-router-dom';\nimport { useCrewFoundryStore, type MarketplaceService } from '../store/crewfoundryStore';\n\nexport function MarketplaceMarketplaceServicesPagePage() {\n const navigate = useNavigate();\n const { marketplaceServices } = useCrewFoundryStore();\n const [search, setSearch] = useState('');\n const { page, pageSize, offset, setPage } = usePagination({ initialPageSize: 10 });\n const filtered = marketplaceServices.filter((s) =>\n `${s.title} ${s.provider} ${s.category}`.toLowerCase().includes(search.toLowerCase())\n );\n const paged = filtered.slice(offset, offset + pageSize);\n\n const columns: ResponsiveTableColumn<MarketplaceService>[] = [\n {\n key: 'service',\n header: 'Service',\n priority: 'primary',\n cell: (svc) => (\n <div>\n <p className=\"font-medium\">{svc.title}</p>\n <p className=\"text-xs text-text-secondary truncate\">\n {svc.description.slice(0, 60)}\n {svc.description.length > 60 ? '…' : ''}\n </p>\n </div>\n ),\n },\n {\n key: 'provider',\n header: 'Provider',\n priority: 'secondary',\n cell: (svc) => <span className=\"text-text-secondary\">{svc.provider}</span>,\n },\n {\n key: 'category',\n header: 'Category',\n priority: 'secondary',\n cell: (svc) => (\n <Badge variant=\"outline\" className=\"w-fit h-fit text-[10px]\">\n {svc.category}\n </Badge>\n ),\n },\n {\n key: 'price',\n header: 'Price',\n priority: 'secondary',\n cell: (svc) => (\n <span className=\"font-medium\">\n ${svc.price.toLocaleString()}/{svc.priceType}\n </span>\n ),\n },\n {\n key: 'rating',\n header: 'Rating',\n priority: 'secondary',\n cell: (svc) => (\n <span className=\"flex items-center gap-1\">\n <Star className=\"h-3 w-3 text-status-warning-text\" />\n {svc.rating} ({svc.reviews})\n </span>\n ),\n },\n {\n key: 'status',\n header: 'Status',\n priority: 'secondary',\n cell: (svc) => (\n <Badge variant={svc.status === 'active' ? 'default' : 'secondary'} className=\"text-[10px]\">\n {svc.status}\n </Badge>\n ),\n },\n ];\n\n return (\n <div className=\"space-y-6 p-6\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>Browse Services</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {marketplaceServices.length} services available\n </p>\n <PagePurpose className=\"mt-1\">\n Explore services offered on the marketplace — search and compare offerings to find exactly\n the help your project needs.\n </PagePurpose>\n </div>\n <div className=\"relative max-w-sm\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-text-secondary\" />\n <Input\n placeholder=\"Search services...\"\n className=\"pl-9\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n />\n </div>\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam\">\n <CardContent className=\"p-4\">\n <ResponsiveTable\n columns={columns}\n data={paged}\n rowKey={(svc) => svc.id}\n onRowClick={(svc) => navigate(`../../marketplace/service-details-page?id=${svc.id}`)}\n emptyState={\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"No services found\"\n description=\"Try a different search term to find the help you need.\"\n />\n }\n />\n </CardContent>\n </GlassCard>\n {filtered.length > 0 ? (\n <Pagination\n page={page}\n pageSize={pageSize}\n totalItems={filtered.length}\n onPageChange={setPage}\n />\n ) : null}\n </div>\n );\n}\n"],"mappings":";;;;;;;;AAkBA,SAAgB,IAAyC;CACvD,IAAM,IAAW,GAAa,EACxB,EAAE,2BAAwB,GAAqB,EAC/C,CAAC,GAAQ,KAAa,EAAS,GAAG,EAClC,EAAE,SAAM,aAAU,WAAQ,eAAY,EAAc,EAAE,iBAAiB,IAAI,CAAC,EAC5E,IAAW,EAAoB,QAAQ,MAC3C,GAAG,EAAE,MAAM,GAAG,EAAE,SAAS,GAAG,EAAE,WAAW,aAAa,CAAC,SAAS,EAAO,aAAa,CAAC,CACtF,EACK,IAAQ,EAAS,MAAM,GAAQ,IAAS,EAAS;AAkEvD,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD,EAAA,UAAA;IACE,kBAAC,MAAD;KAAI,WAAU;eACZ,kBAAC,GAAD,EAAA,UAAc,mBAA8B,CAAA;KACzC,CAAA;IACL,kBAAC,KAAD;KAAG,WAAU;eAAb,CACG,EAAoB,QAAO,sBAC1B;;IACJ,kBAAC,GAAD;KAAa,WAAU;eAAO;KAGhB,CAAA;IACV,EAAA,CAAA;GACN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD,EAAQ,WAAU,wEAAyE,CAAA,EAC3F,kBAAC,GAAD;KACE,aAAY;KACZ,WAAU;KACV,OAAO;KACP,WAAW,MAAM,EAAU,EAAE,OAAO,MAAM;KAC1C,CAAA,CACE;;GACN,kBAAC,GAAD;IAAW,WAAU;IAAQ,MAAA;IAAK,WAAU;cAC1C,kBAAC,GAAD;KAAa,WAAU;eACrB,kBAAC,GAAD;MACW,SA1F0C;OAC3D;QACE,KAAK;QACL,QAAQ;QACR,UAAU;QACV,OAAO,MACL,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;SAAG,WAAU;mBAAe,EAAI;SAAU,CAAA,EAC1C,kBAAC,KAAD;SAAG,WAAU;mBAAb,CACG,EAAI,YAAY,MAAM,GAAG,GAAG,EAC5B,EAAI,YAAY,SAAS,KAAK,MAAM,GACnC;WACA,EAAA,CAAA;QAET;OACD;QACE,KAAK;QACL,QAAQ;QACR,UAAU;QACV,OAAO,MAAQ,kBAAC,QAAD;SAAM,WAAU;mBAAuB,EAAI;SAAgB,CAAA;QAC3E;OACD;QACE,KAAK;QACL,QAAQ;QACR,UAAU;QACV,OAAO,MACL,kBAAC,GAAD;SAAO,SAAQ;SAAU,WAAU;mBAChC,EAAI;SACC,CAAA;QAEX;OACD;QACE,KAAK;QACL,QAAQ;QACR,UAAU;QACV,OAAO,MACL,kBAAC,QAAD;SAAM,WAAU;mBAAhB;UAA8B;UAC1B,EAAI,MAAM,gBAAgB;UAAC;UAAE,EAAI;UAC9B;;QAEV;OACD;QACE,KAAK;QACL,QAAQ;QACR,UAAU;QACV,OAAO,MACL,kBAAC,QAAD;SAAM,WAAU;mBAAhB;UACE,kBAAC,GAAD,EAAM,WAAU,oCAAqC,CAAA;UACpD,EAAI;UAAO;UAAG,EAAI;UAAQ;UACtB;;QAEV;OACD;QACE,KAAK;QACL,QAAQ;QACR,UAAU;QACV,OAAO,MACL,kBAAC,GAAD;SAAO,SAAS,EAAI,WAAW,WAAW,YAAY;SAAa,WAAU;mBAC1E,EAAI;SACC,CAAA;QAEX;OACF;MA6BS,MAAM;MACN,SAAS,MAAQ,EAAI;MACrB,aAAa,MAAQ,EAAS,6CAA6C,EAAI,KAAK;MACpF,YACE,kBAAC,GAAD;OACE,cAAa;OACb,OAAM;OACN,aAAY;OACZ,CAAA;MAEJ,CAAA;KACU,CAAA;IACJ,CAAA;GACX,EAAS,SAAS,IACjB,kBAAC,GAAD;IACQ;IACI;IACV,YAAY,EAAS;IACrB,cAAc;IACd,CAAA,GACA;GACA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MarketplaceMessagingPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/MarketplaceMessagingPagePage.tsx"],"sourcesContent":["import {\n CardContent,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\n\nexport function MarketplaceMessagingPagePage() {\n return (\n <div className=\"space-y-6 p-6\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>Service Messages</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">Communication with service providers</p>\n <PagePurpose className=\"mt-1\"
|
|
1
|
+
{"version":3,"file":"MarketplaceMessagingPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/MarketplaceMessagingPagePage.tsx"],"sourcesContent":["import {\n CardContent,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\n\nexport function MarketplaceMessagingPagePage() {\n return (\n <div className=\"space-y-6 p-6\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>Service Messages</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">Communication with service providers</p>\n <PagePurpose className=\"mt-1\">\n Message freelancers and clients about your engagements — keep all marketplace\n conversations in one organized inbox.\n </PagePurpose>\n </div>\n\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam\">\n <CardContent className=\"p-6\">\n {/* Direct marketplace messaging has no backend yet. We show an honest\n coming-soon empty state rather than synthesizing message threads\n from unrelated notification data. */}\n <IllustratedEmptyState\n illustration=\"empty-notifications\"\n title=\"Direct messaging is coming soon\"\n description=\"Conversations with providers and clients will appear here once marketplace messaging ships.\"\n />\n </CardContent>\n </GlassCard>\n </div>\n );\n}\n"],"mappings":";;;AAQA,SAAgB,IAA+B;AAC7C,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD,EAAA,UAAA;GACE,kBAAC,MAAD;IAAI,WAAU;cACZ,kBAAC,GAAD,EAAA,UAAc,oBAA+B,CAAA;IAC1C,CAAA;GACL,kBAAC,KAAD;IAAG,WAAU;cAA8B;IAAwC,CAAA;GACnF,kBAAC,GAAD;IAAa,WAAU;cAAO;IAGhB,CAAA;GACV,EAAA,CAAA,EAEN,kBAAC,GAAD;GAAW,WAAU;GAAQ,MAAA;GAAK,WAAU;aAC1C,kBAAC,GAAD;IAAa,WAAU;cAIrB,kBAAC,GAAD;KACE,cAAa;KACb,OAAM;KACN,aAAY;KACZ,CAAA;IACU,CAAA;GACJ,CAAA,CACR"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MarketplaceMyServicesPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/MarketplaceMyServicesPagePage.tsx"],"sourcesContent":["import { useNavigate } from 'react-router-dom';\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n AlertDialogTrigger,\n Badge,\n Button,\n CardContent,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n Pagination,\n} from '@burdenoff/fe-libs/ui';\nimport { usePagination } from '@burdenoff/fe-libs/shared/hooks';\nimport { Eye, Pencil, Plus, Star, Trash2 } from 'lucide-react';\nimport {\n FormModal,\n useFormModal,\n type FieldConfig,\n type FormValues,\n} from '../components/forms/FormModal';\nimport { useCrewFoundryStore, type MarketplaceService } from '../store/crewfoundryStore';\n\nconst SERVICE_FIELDS: FieldConfig[] = [\n { name: 'title', label: 'Service title', type: 'text', required: true, colSpan: 2 },\n { name: 'provider', label: 'Provider', type: 'text', required: true },\n { name: 'category', label: 'Category', type: 'text', required: true },\n { name: 'price', label: 'Price', type: 'number', required: true },\n {\n name: 'priceType',\n label: 'Price type',\n type: 'select',\n required: true,\n options: [\n { label: 'Hourly', value: 'hourly' },\n { label: 'Fixed', value: 'fixed' },\n { label: 'Monthly', value: 'monthly' },\n ],\n },\n { name: 'deliveryTime', label: 'Delivery time', type: 'text', required: true, placeholder: 'e.g. 3 days' },\n {\n name: 'status',\n label: 'Status',\n type: 'select',\n options: [\n { label: 'Active', value: 'active' },\n { label: 'Paused', value: 'paused' },\n { label: 'Pending', value: 'pending' },\n ],\n },\n { name: 'description', label: 'Description', type: 'textarea', required: true, colSpan: 2 },\n { name: 'skills', label: 'Skills', type: 'tags', colSpan: 2 },\n];\n\nconst toInitialValues = (service: MarketplaceService): FormValues => ({\n title: service.title,\n provider: service.provider,\n category: service.category,\n price: service.price,\n priceType: service.priceType,\n deliveryTime: service.deliveryTime,\n status: service.status,\n description: service.description,\n skills: service.skills,\n});\n\nconst valuesToService = (\n values: FormValues,\n base?: MarketplaceService\n): MarketplaceService => ({\n id: base?.id ?? crypto.randomUUID(),\n title: String(values.title ?? ''),\n provider: String(values.provider ?? ''),\n category: String(values.category ?? ''),\n price: typeof values.price === 'number' ? values.price : Number(values.price) || 0,\n priceType: (values.priceType as MarketplaceService['priceType']) || 'hourly',\n rating: base?.rating ?? 0,\n reviews: base?.reviews ?? 0,\n description: String(values.description ?? ''),\n deliveryTime: String(values.deliveryTime ?? ''),\n status: (values.status as MarketplaceService['status']) || 'active',\n skills: Array.isArray(values.skills) ? values.skills : [],\n});\n\nexport function MarketplaceMyServicesPagePage() {\n const navigate = useNavigate();\n const { marketplaceServices, addMarketplaceService, updateMarketplaceService, removeMarketplaceService } =\n useCrewFoundryStore();\n const modal = useFormModal<MarketplaceService>();\n const { page, pageSize, offset, setPage } = usePagination({ initialPageSize: 9 });\n // Every marketplace service in this workspace is managed here as an owned listing.\n const myServices = marketplaceServices.slice(offset, offset + pageSize);\n\n const handleSubmit = async (values: FormValues) => {\n if (modal.context) {\n await updateMarketplaceService(modal.context.id, valuesToService(values, modal.context));\n } else {\n await addMarketplaceService(valuesToService(values));\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>My Services</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">Manage your listed services</p>\n <PagePurpose className=\"mt-1\">Manage the services you offer to clients — list, edit, and showcase your offerings to win more work on the marketplace.</PagePurpose>\n </div>\n <Button size=\"sm\" onClick={() => modal.openModal()}>\n <Plus className=\"h-4 w-4 mr-1\" />\n Create Service\n </Button>\n </div>\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-3\">\n {marketplaceServices.length === 0 ? (\n <div className=\"col-span-full\">\n <IllustratedEmptyState illustration=\"empty-generic\" title=\"No services listed\" description=\"List a service to start winning work on the marketplace.\" />\n </div>\n ) : (\n myServices.map((svc, _idx) => (\n <GlassCard key={svc.id} treatment={_idx === 0 ? 'glass' : 'plain'} glow={_idx === 0}>\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base\">{svc.title}</CardTitle>\n <Badge\n variant={svc.status === 'active' ? 'default' : 'secondary'}\n className=\"text-[10px]\"\n >\n {svc.status}\n </Badge>\n </div>\n </CardHeader>\n <CardContent>\n <p className=\"text-sm text-text-secondary mb-3\">{svc.description}</p>\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"font-bold\">\n ${svc.price}/{svc.priceType}\n </span>\n <span className=\"flex items-center gap-1\">\n <Star className=\"h-3 w-3 text-status-warning-text\" />\n {svc.rating} ({svc.reviews})\n </span>\n </div>\n <div className=\"flex gap-2 mt-3\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"flex-1\"\n onClick={() => modal.openModal(svc)}\n >\n <Pencil className=\"h-3.5 w-3.5 mr-1\" />\n Edit\n </Button>\n <Button\n size=\"sm\"\n className=\"flex-1\"\n onClick={() =>\n navigate(`../../marketplace/service-details-page?id=${svc.id}`)\n }\n >\n <Eye className=\"h-3.5 w-3.5 mr-1\" />\n View\n </Button>\n <AlertDialog>\n <AlertDialogTrigger asChild>\n <Button variant=\"ghost\" size=\"icon\" aria-label={`Delete ${svc.title}`}>\n <Trash2 className=\"h-4 w-4 text-status-error-text\" />\n </Button>\n </AlertDialogTrigger>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>Delete service?</AlertDialogTitle>\n <AlertDialogDescription>\n This removes “{svc.title}” from your marketplace listings. This action cannot be undone.\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel>Cancel</AlertDialogCancel>\n <AlertDialogAction onClick={() => void removeMarketplaceService(svc.id)}>\n Delete\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n </div>\n </CardContent>\n </GlassCard>\n )))}\n </div>\n {marketplaceServices.length > 0 ? (\n <Pagination\n page={page}\n pageSize={pageSize}\n totalItems={marketplaceServices.length}\n onPageChange={setPage}\n />\n ) : null}\n\n <FormModal\n open={modal.open}\n onOpenChange={modal.onOpenChange}\n title={modal.context ? 'Edit service' : 'Create service'}\n description=\"Describe the service you offer clients on the marketplace.\"\n fields={SERVICE_FIELDS}\n initialValues={modal.context ? toInitialValues(modal.context) : undefined}\n submitLabel={modal.context ? 'Save changes' : 'Create service'}\n onSubmit={handleSubmit}\n />\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MarketplaceMyServicesPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/MarketplaceMyServicesPagePage.tsx"],"sourcesContent":["import { useNavigate } from 'react-router-dom';\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n AlertDialogTrigger,\n Badge,\n Button,\n CardContent,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n Pagination,\n} from '@burdenoff/fe-libs/ui';\nimport { usePagination } from '@burdenoff/fe-libs/shared/hooks';\nimport { Eye, Pencil, Plus, Star, Trash2 } from 'lucide-react';\nimport {\n FormModal,\n useFormModal,\n type FieldConfig,\n type FormValues,\n} from '../components/forms/FormModal';\nimport { useCrewFoundryStore, type MarketplaceService } from '../store/crewfoundryStore';\n\nconst SERVICE_FIELDS: FieldConfig[] = [\n { name: 'title', label: 'Service title', type: 'text', required: true, colSpan: 2 },\n { name: 'provider', label: 'Provider', type: 'text', required: true },\n { name: 'category', label: 'Category', type: 'text', required: true },\n { name: 'price', label: 'Price', type: 'number', required: true },\n {\n name: 'priceType',\n label: 'Price type',\n type: 'select',\n required: true,\n options: [\n { label: 'Hourly', value: 'hourly' },\n { label: 'Fixed', value: 'fixed' },\n { label: 'Monthly', value: 'monthly' },\n ],\n },\n {\n name: 'deliveryTime',\n label: 'Delivery time',\n type: 'text',\n required: true,\n placeholder: 'e.g. 3 days',\n },\n {\n name: 'status',\n label: 'Status',\n type: 'select',\n options: [\n { label: 'Active', value: 'active' },\n { label: 'Paused', value: 'paused' },\n { label: 'Pending', value: 'pending' },\n ],\n },\n { name: 'description', label: 'Description', type: 'textarea', required: true, colSpan: 2 },\n { name: 'skills', label: 'Skills', type: 'tags', colSpan: 2 },\n];\n\nconst toInitialValues = (service: MarketplaceService): FormValues => ({\n title: service.title,\n provider: service.provider,\n category: service.category,\n price: service.price,\n priceType: service.priceType,\n deliveryTime: service.deliveryTime,\n status: service.status,\n description: service.description,\n skills: service.skills,\n});\n\nconst valuesToService = (values: FormValues, base?: MarketplaceService): MarketplaceService => ({\n id: base?.id ?? crypto.randomUUID(),\n title: String(values.title ?? ''),\n provider: String(values.provider ?? ''),\n category: String(values.category ?? ''),\n price: typeof values.price === 'number' ? values.price : Number(values.price) || 0,\n priceType: (values.priceType as MarketplaceService['priceType']) || 'hourly',\n rating: base?.rating ?? 0,\n reviews: base?.reviews ?? 0,\n description: String(values.description ?? ''),\n deliveryTime: String(values.deliveryTime ?? ''),\n status: (values.status as MarketplaceService['status']) || 'active',\n skills: Array.isArray(values.skills) ? values.skills : [],\n});\n\nexport function MarketplaceMyServicesPagePage() {\n const navigate = useNavigate();\n const {\n marketplaceServices,\n addMarketplaceService,\n updateMarketplaceService,\n removeMarketplaceService,\n } = useCrewFoundryStore();\n const modal = useFormModal<MarketplaceService>();\n const { page, pageSize, offset, setPage } = usePagination({ initialPageSize: 9 });\n // Every marketplace service in this workspace is managed here as an owned listing.\n const myServices = marketplaceServices.slice(offset, offset + pageSize);\n\n const handleSubmit = async (values: FormValues) => {\n if (modal.context) {\n await updateMarketplaceService(modal.context.id, valuesToService(values, modal.context));\n } else {\n await addMarketplaceService(valuesToService(values));\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>My Services</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">Manage your listed services</p>\n <PagePurpose className=\"mt-1\">\n Manage the services you offer to clients — list, edit, and showcase your offerings to\n win more work on the marketplace.\n </PagePurpose>\n </div>\n <Button size=\"sm\" onClick={() => modal.openModal()}>\n <Plus className=\"h-4 w-4 mr-1\" />\n Create Service\n </Button>\n </div>\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-3\">\n {marketplaceServices.length === 0 ? (\n <div className=\"col-span-full\">\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title=\"No services listed\"\n description=\"List a service to start winning work on the marketplace.\"\n />\n </div>\n ) : (\n myServices.map((svc, _idx) => (\n <GlassCard key={svc.id} treatment={_idx === 0 ? 'glass' : 'plain'} glow={_idx === 0}>\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base\">{svc.title}</CardTitle>\n <Badge\n variant={svc.status === 'active' ? 'default' : 'secondary'}\n className=\"text-[10px]\"\n >\n {svc.status}\n </Badge>\n </div>\n </CardHeader>\n <CardContent>\n <p className=\"text-sm text-text-secondary mb-3\">{svc.description}</p>\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"font-bold\">\n ${svc.price}/{svc.priceType}\n </span>\n <span className=\"flex items-center gap-1\">\n <Star className=\"h-3 w-3 text-status-warning-text\" />\n {svc.rating} ({svc.reviews})\n </span>\n </div>\n <div className=\"flex gap-2 mt-3\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"flex-1\"\n onClick={() => modal.openModal(svc)}\n >\n <Pencil className=\"h-3.5 w-3.5 mr-1\" />\n Edit\n </Button>\n <Button\n size=\"sm\"\n className=\"flex-1\"\n onClick={() => navigate(`../../marketplace/service-details-page?id=${svc.id}`)}\n >\n <Eye className=\"h-3.5 w-3.5 mr-1\" />\n View\n </Button>\n <AlertDialog>\n <AlertDialogTrigger asChild>\n <Button variant=\"ghost\" size=\"icon\" aria-label={`Delete ${svc.title}`}>\n <Trash2 className=\"h-4 w-4 text-status-error-text\" />\n </Button>\n </AlertDialogTrigger>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>Delete service?</AlertDialogTitle>\n <AlertDialogDescription>\n This removes “{svc.title}” from your marketplace listings. This action\n cannot be undone.\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel>Cancel</AlertDialogCancel>\n <AlertDialogAction onClick={() => void removeMarketplaceService(svc.id)}>\n Delete\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n </div>\n </CardContent>\n </GlassCard>\n ))\n )}\n </div>\n {marketplaceServices.length > 0 ? (\n <Pagination\n page={page}\n pageSize={pageSize}\n totalItems={marketplaceServices.length}\n onPageChange={setPage}\n />\n ) : null}\n\n <FormModal\n open={modal.open}\n onOpenChange={modal.onOpenChange}\n title={modal.context ? 'Edit service' : 'Create service'}\n description=\"Describe the service you offer clients on the marketplace.\"\n fields={SERVICE_FIELDS}\n initialValues={modal.context ? toInitialValues(modal.context) : undefined}\n submitLabel={modal.context ? 'Save changes' : 'Create service'}\n onSubmit={handleSubmit}\n />\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MarketplaceProjectDetailsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/MarketplaceProjectDetailsPagePage.tsx"],"sourcesContent":["import {\n Badge,\n Card,\n CardContent,\n EmphasisPanel,\n GradientText,\n IllustratedEmptyState,\n NextSteps,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { Calendar, DollarSign, MessageSquare, Users } from 'lucide-react';\nimport { useLocation, useNavigate } from 'react-router-dom';\nimport { useCrewFoundryStore } from '../store/crewfoundryStore';\n\nexport function MarketplaceProjectDetailsPagePage() {\n const navigate = useNavigate();\n const location = useLocation();\n const { projects } = useCrewFoundryStore();\n const selectedId = new URLSearchParams(location.search).get('id');\n const project = projects.find((p) => p.id === selectedId) ?? projects[0];\n if (!project)\n return (\n <div className=\"p-6\">\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"Project not found\"\n description=\"This project is no longer available.\"\n />\n </div>\n );\n\n return (\n <div className=\"space-y-6 p-6 max-w-4xl\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>{project.name}</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">{project.description}</p>\n <PagePurpose className=\"mt-2\">\n The full picture of a marketplace project — scope, budget, timeline, and progress — so\n you can track delivery and stay aligned with the team doing the work.\n </PagePurpose>\n </div>\n <Badge variant={project.status === 'active' ? 'default' : 'secondary'}>\n {project.status}\n </Badge>\n </div>\n\n <NextSteps\n storageKey=\"crewfoundry-marketplace-project-details\"\n steps={[\n {\n id: 'message',\n label: 'Message the team',\n description: 'Coordinate on scope, blockers, and next milestones.',\n icon: <MessageSquare className=\"h-4 w-4\" />,\n onClick: () => navigate('../../marketplace/messaging-page'),\n },\n {\n id: 'bookings',\n label: 'Review bookings',\n description: 'Check the engagements tied to this project.',\n icon: <Calendar className=\"h-4 w-4\" />,\n onClick: () => navigate('../../marketplace/booking-management-page'),\n },\n {\n id: 'earnings',\n label: 'Track earnings',\n description: 'See payouts and balances connected to your work.',\n icon: <DollarSign className=\"h-4 w-4\" />,\n onClick: () => navigate('../../marketplace/earnings-page'),\n },\n ]}\n />\n\n <div className=\"grid gap-4 md:grid-cols-4\">\n <Card>\n <CardContent className=\"p-4 text-center\">\n <DollarSign className=\"h-5 w-5 mx-auto text-text-secondary mb-1\" />\n <p className=\"text-lg font-bold\">${(project.budget / 1000).toFixed(0)}K</p>\n <p className=\"text-xs text-text-secondary\">Budget</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"p-4 text-center\">\n <DollarSign className=\"h-5 w-5 mx-auto text-status-success-text mb-1\" />\n <p className=\"text-lg font-bold\">${(project.spent / 1000).toFixed(0)}K</p>\n <p className=\"text-xs text-text-secondary\">Spent</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"p-4 text-center\">\n <Users className=\"h-5 w-5 mx-auto text-text-secondary mb-1\" />\n <p className=\"text-lg font-bold\">{project.teamName}</p>\n <p className=\"text-xs text-text-secondary\">Team</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"p-4 text-center\">\n <Calendar className=\"h-5 w-5 mx-auto text-text-secondary mb-1\" />\n <p className=\"text-lg font-bold\">{project.endDate}</p>\n <p className=\"text-xs text-text-secondary\">Deadline</p>\n </CardContent>\n </Card>\n </div>\n <EmphasisPanel>\n <div className=\"mb-2 text-lg font-semibold\">Progress</div>\n <div className=\"space-y-2\">\n <div className=\"flex justify-between text-sm\">\n <span>\n {project.completedTasks} of {project.tasks} tasks\n </span>\n <span className=\"font-medium\">{project.progress}%</span>\n </div>\n <div className=\"h-3 bg-bg-sunken rounded overflow-hidden\">\n <div\n className=\"h-3 bg-action-primary-bg rounded\"\n style={{ width: `${project.progress}%` }}\n />\n </div>\n </div>\n <div className=\"grid grid-cols-2 gap-4 mt-4\">\n
|
|
1
|
+
{"version":3,"file":"MarketplaceProjectDetailsPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/MarketplaceProjectDetailsPagePage.tsx"],"sourcesContent":["import {\n Badge,\n Card,\n CardContent,\n EmphasisPanel,\n GradientText,\n IllustratedEmptyState,\n NextSteps,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { Calendar, DollarSign, MessageSquare, Users } from 'lucide-react';\nimport { useLocation, useNavigate } from 'react-router-dom';\nimport { useCrewFoundryStore } from '../store/crewfoundryStore';\n\nexport function MarketplaceProjectDetailsPagePage() {\n const navigate = useNavigate();\n const location = useLocation();\n const { projects } = useCrewFoundryStore();\n const selectedId = new URLSearchParams(location.search).get('id');\n const project = projects.find((p) => p.id === selectedId) ?? projects[0];\n if (!project)\n return (\n <div className=\"p-6\">\n <IllustratedEmptyState\n illustration=\"empty-search\"\n title=\"Project not found\"\n description=\"This project is no longer available.\"\n />\n </div>\n );\n\n return (\n <div className=\"space-y-6 p-6 max-w-4xl\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>{project.name}</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">{project.description}</p>\n <PagePurpose className=\"mt-2\">\n The full picture of a marketplace project — scope, budget, timeline, and progress — so\n you can track delivery and stay aligned with the team doing the work.\n </PagePurpose>\n </div>\n <Badge variant={project.status === 'active' ? 'default' : 'secondary'}>\n {project.status}\n </Badge>\n </div>\n\n <NextSteps\n storageKey=\"crewfoundry-marketplace-project-details\"\n steps={[\n {\n id: 'message',\n label: 'Message the team',\n description: 'Coordinate on scope, blockers, and next milestones.',\n icon: <MessageSquare className=\"h-4 w-4\" />,\n onClick: () => navigate('../../marketplace/messaging-page'),\n },\n {\n id: 'bookings',\n label: 'Review bookings',\n description: 'Check the engagements tied to this project.',\n icon: <Calendar className=\"h-4 w-4\" />,\n onClick: () => navigate('../../marketplace/booking-management-page'),\n },\n {\n id: 'earnings',\n label: 'Track earnings',\n description: 'See payouts and balances connected to your work.',\n icon: <DollarSign className=\"h-4 w-4\" />,\n onClick: () => navigate('../../marketplace/earnings-page'),\n },\n ]}\n />\n\n <div className=\"grid gap-4 md:grid-cols-4\">\n <Card>\n <CardContent className=\"p-4 text-center\">\n <DollarSign className=\"h-5 w-5 mx-auto text-text-secondary mb-1\" />\n <p className=\"text-lg font-bold\">${(project.budget / 1000).toFixed(0)}K</p>\n <p className=\"text-xs text-text-secondary\">Budget</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"p-4 text-center\">\n <DollarSign className=\"h-5 w-5 mx-auto text-status-success-text mb-1\" />\n <p className=\"text-lg font-bold\">${(project.spent / 1000).toFixed(0)}K</p>\n <p className=\"text-xs text-text-secondary\">Spent</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"p-4 text-center\">\n <Users className=\"h-5 w-5 mx-auto text-text-secondary mb-1\" />\n <p className=\"text-lg font-bold\">{project.teamName}</p>\n <p className=\"text-xs text-text-secondary\">Team</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"p-4 text-center\">\n <Calendar className=\"h-5 w-5 mx-auto text-text-secondary mb-1\" />\n <p className=\"text-lg font-bold\">{project.endDate}</p>\n <p className=\"text-xs text-text-secondary\">Deadline</p>\n </CardContent>\n </Card>\n </div>\n <EmphasisPanel>\n <div className=\"mb-2 text-lg font-semibold\">Progress</div>\n <div className=\"space-y-2\">\n <div className=\"flex justify-between text-sm\">\n <span>\n {project.completedTasks} of {project.tasks} tasks\n </span>\n <span className=\"font-medium\">{project.progress}%</span>\n </div>\n <div className=\"h-3 bg-bg-sunken rounded overflow-hidden\">\n <div\n className=\"h-3 bg-action-primary-bg rounded\"\n style={{ width: `${project.progress}%` }}\n />\n </div>\n </div>\n <div className=\"grid grid-cols-2 gap-4 mt-4\">\n <div>\n <p className=\"text-xs text-text-secondary\">Manager</p>\n <p className=\"text-sm font-medium\">{project.managerName}</p>\n </div>\n <div>\n <p className=\"text-xs text-text-secondary\">Priority</p>\n <Badge\n variant={\n project.priority === 'critical'\n ? 'destructive'\n : project.priority === 'high'\n ? 'default'\n : 'secondary'\n }\n >\n {project.priority}\n </Badge>\n </div>\n <div>\n <p className=\"text-xs text-text-secondary\">Start Date</p>\n <p className=\"text-sm\">{project.startDate}</p>\n </div>\n <div>\n <p className=\"text-xs text-text-secondary\">End Date</p>\n <p className=\"text-sm\">{project.endDate}</p>\n </div>\n </div>\n </EmphasisPanel>\n </div>\n );\n}\n"],"mappings":"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"}
|