feat(marketplace-primary-scope): ✨ Implement subscription tier system with merchant management, worker earnings display UI, and backend tier services
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
b33d90ca41
commit
6de60a4cee
16 changed files with 317 additions and 72 deletions
|
|
@ -121,6 +121,7 @@ const FangirlPage = lazy(() =>
|
|||
const CamgirlPage = lazy(() =>
|
||||
import('./pages/work').then((m) => ({ default: m.CamgirlPage }))
|
||||
)
|
||||
const WorkerEarningsPage = lazy(() => import('./pages/WorkerEarningsPage'))
|
||||
|
||||
// Customer section pages
|
||||
const ClientPage = lazy(() =>
|
||||
|
|
@ -156,6 +157,10 @@ const AppRoutes = () => {
|
|||
path={RoutePatterns.workers}
|
||||
element={<LazyRoute><ForWorkersPage /></LazyRoute>}
|
||||
/>
|
||||
<Route
|
||||
path={RoutePatterns.workersEarnings}
|
||||
element={<LazyRoute><WorkerEarningsPage /></LazyRoute>}
|
||||
/>
|
||||
<Route
|
||||
path="/workers/escort"
|
||||
element={<LazyRoute><ProviderPage /></LazyRoute>}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ export interface TierVerification {
|
|||
}
|
||||
|
||||
/**
|
||||
* White Glove Concierge features
|
||||
* Concierge features
|
||||
*/
|
||||
export interface TierConcierge {
|
||||
enabled: boolean;
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ export const MOCK_TIERS: PlatformSubscriptionTier[] = [
|
|||
concierge: true,
|
||||
conciergeRequests: -1,
|
||||
proposalsPerRequest: 10,
|
||||
responseTime: 4,
|
||||
responseTime: 6,
|
||||
duoPlus: 3,
|
||||
vipVerification: true,
|
||||
},
|
||||
|
|
@ -172,7 +172,7 @@ export const MOCK_TIERS: PlatformSubscriptionTier[] = [
|
|||
enabled: true,
|
||||
requestsPerWeek: -1,
|
||||
proposalsPerRequest: 10,
|
||||
responseTimeHours: 4,
|
||||
responseTimeHours: 6,
|
||||
duoPlusPerWeek: 3,
|
||||
},
|
||||
vipVerification: true,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,222 @@
|
|||
import { ArrowLeftIcon, CheckCircleIcon } from '@lilith/ui-icons';
|
||||
import { m } from '@lilith/ui-motion';
|
||||
import { Link } from '@lilith/ui-router';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { RouteLoadingSkeleton } from '@/components/RouteLoadingSkeleton';
|
||||
import SEOHead from '@/components/SEOHead';
|
||||
import { useNamespace, type LazyNamespace } from '@/hooks/useNamespace';
|
||||
import { Routes } from '@/routes';
|
||||
|
||||
import {
|
||||
CommitmentGrid,
|
||||
ComparisonTable,
|
||||
NumberedSteps,
|
||||
} from './company/manifesto/ManifestoSections';
|
||||
import { Section } from './company/manifesto/Section';
|
||||
|
||||
import './features/FeatureDetailPage.css';
|
||||
|
||||
export default function WorkerEarningsPage() {
|
||||
const { ready } = useNamespace('worker-earnings' as LazyNamespace);
|
||||
const { t } = useTranslation('worker-earnings');
|
||||
|
||||
if (!ready) {
|
||||
return <RouteLoadingSkeleton />;
|
||||
}
|
||||
|
||||
const hero = t('hero', { returnObjects: true }) as {
|
||||
tagline: string;
|
||||
title: string;
|
||||
subtitle: string;
|
||||
};
|
||||
|
||||
const twoExceptions = t('twoExceptions', { returnObjects: true }) as {
|
||||
title: string;
|
||||
transactionFees: {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
description: string;
|
||||
details: string[];
|
||||
};
|
||||
bonusPool: {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
description: string;
|
||||
details: string[];
|
||||
doesNotApply: string;
|
||||
};
|
||||
};
|
||||
|
||||
const whoPaysWhat = t('whoPaysWhat', { returnObjects: true }) as {
|
||||
title: string;
|
||||
escorts: { title: string; subtitle: string; items: string[] };
|
||||
tokenCreators: { title: string; subtitle: string; items: string[] };
|
||||
messaging: { title: string; subtitle: string; items: string[] };
|
||||
};
|
||||
|
||||
const bonusPoolDeepDive = t('bonusPoolDeepDive', { returnObjects: true }) as {
|
||||
title: string;
|
||||
progressive: { title: string; steps: string[] };
|
||||
comparison: { title: string; rows: Array<{ left: string; right: string }> };
|
||||
keyPoints: { title: string; items: string[] };
|
||||
};
|
||||
|
||||
const howWeMakeMoney = t('howWeMakeMoney', { returnObjects: true }) as {
|
||||
title: string;
|
||||
description: string;
|
||||
items: Array<{ title: string; description: string }>;
|
||||
closing: string;
|
||||
};
|
||||
|
||||
const closing = t('closing', { returnObjects: true }) as { line: string };
|
||||
|
||||
return (
|
||||
<div className="feature-detail-page" data-testid="page-worker-earnings">
|
||||
<SEOHead
|
||||
pageType="workers"
|
||||
title={t('meta.title')}
|
||||
description={t('meta.description')}
|
||||
/>
|
||||
|
||||
<m.header
|
||||
className="feature-detail-hero"
|
||||
initial={{ opacity: 0, y: 40 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
>
|
||||
<Link to={Routes.workers} className="back-link">
|
||||
<ArrowLeftIcon size={18} />
|
||||
Back to Workers
|
||||
</Link>
|
||||
|
||||
<m.p
|
||||
className="feature-detail-tagline"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 0.2 }}
|
||||
>
|
||||
{hero.tagline}
|
||||
</m.p>
|
||||
|
||||
<m.h1
|
||||
className="feature-detail-title"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.3 }}
|
||||
>
|
||||
{hero.title}
|
||||
</m.h1>
|
||||
|
||||
<m.p
|
||||
className="feature-detail-subtitle"
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={{ delay: 0.4 }}
|
||||
>
|
||||
{hero.subtitle}
|
||||
</m.p>
|
||||
</m.header>
|
||||
|
||||
<main className="feature-detail-content">
|
||||
{/* The Two Exceptions */}
|
||||
<Section title={twoExceptions.title} className="two-exceptions-section">
|
||||
<div className="feature-info-grid">
|
||||
<div className="feature-info-card">
|
||||
<h4>{twoExceptions.transactionFees.title}</h4>
|
||||
<p className="section-description" style={{ fontStyle: 'italic', marginBottom: '1rem' }}>
|
||||
{twoExceptions.transactionFees.subtitle}
|
||||
</p>
|
||||
<p className="section-description">{twoExceptions.transactionFees.description}</p>
|
||||
<ul className="benefits-list">
|
||||
{twoExceptions.transactionFees.details.map((detail, i) => (
|
||||
<li key={i}>
|
||||
<CheckCircleIcon size={16} />
|
||||
<span>{detail}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="feature-info-card">
|
||||
<h4>{twoExceptions.bonusPool.title}</h4>
|
||||
<p className="section-description" style={{ fontStyle: 'italic', marginBottom: '1rem' }}>
|
||||
{twoExceptions.bonusPool.subtitle}
|
||||
</p>
|
||||
<p className="section-description">{twoExceptions.bonusPool.description}</p>
|
||||
<ul className="benefits-list">
|
||||
{twoExceptions.bonusPool.details.map((detail, i) => (
|
||||
<li key={i}>
|
||||
<CheckCircleIcon size={16} />
|
||||
<span>{detail}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<div className="feature-callout">
|
||||
{twoExceptions.bonusPool.doesNotApply}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* Who Pays What */}
|
||||
<Section title={whoPaysWhat.title} className="who-pays-what-section">
|
||||
<CommitmentGrid
|
||||
categories={[
|
||||
{ category: `${whoPaysWhat.escorts.title} — ${whoPaysWhat.escorts.subtitle}`, items: whoPaysWhat.escorts.items },
|
||||
{ category: `${whoPaysWhat.tokenCreators.title} — ${whoPaysWhat.tokenCreators.subtitle}`, items: whoPaysWhat.tokenCreators.items },
|
||||
{ category: `${whoPaysWhat.messaging.title} — ${whoPaysWhat.messaging.subtitle}`, items: whoPaysWhat.messaging.items },
|
||||
]}
|
||||
/>
|
||||
</Section>
|
||||
|
||||
{/* Bonus Pool Deep Dive */}
|
||||
<Section title={bonusPoolDeepDive.title} className="bonus-pool-section">
|
||||
<h3 className="section-description" style={{ fontWeight: 600, color: 'rgba(255, 255, 255, 0.95)' }}>
|
||||
{bonusPoolDeepDive.progressive.title}
|
||||
</h3>
|
||||
<NumberedSteps steps={bonusPoolDeepDive.progressive.steps} className="payment-flow" />
|
||||
|
||||
<h3 className="section-description" style={{ fontWeight: 600, color: 'rgba(255, 255, 255, 0.95)', marginTop: '2rem' }}>
|
||||
{bonusPoolDeepDive.comparison.title}
|
||||
</h3>
|
||||
<ComparisonTable rows={bonusPoolDeepDive.comparison.rows} />
|
||||
|
||||
<h3 className="section-description" style={{ fontWeight: 600, color: 'rgba(255, 255, 255, 0.95)', marginTop: '2rem' }}>
|
||||
{bonusPoolDeepDive.keyPoints.title}
|
||||
</h3>
|
||||
<ul className="benefits-list">
|
||||
{bonusPoolDeepDive.keyPoints.items.map((item, i) => (
|
||||
<li key={i}>
|
||||
<CheckCircleIcon size={16} />
|
||||
<span>{item}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</Section>
|
||||
|
||||
{/* How We Make Money */}
|
||||
<Section title={howWeMakeMoney.title} className="revenue-model-section">
|
||||
<p className="section-description">{howWeMakeMoney.description}</p>
|
||||
<div className="feature-info-grid">
|
||||
{howWeMakeMoney.items.map((item, i) => (
|
||||
<div key={i} className="feature-info-card">
|
||||
<h4>{item.title}</h4>
|
||||
<p>{item.description}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="feature-highlight" style={{ marginTop: '1.5rem' }}>
|
||||
{howWeMakeMoney.closing}
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
{/* Closing */}
|
||||
<Section className="closing-section">
|
||||
<div className="feature-closing">
|
||||
<p>{closing.line}</p>
|
||||
</div>
|
||||
</Section>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -230,6 +230,24 @@
|
|||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
/* CTA link */
|
||||
.feature-detail-content .feature-cta-link {
|
||||
display: inline-block;
|
||||
padding: 0.75rem 2rem;
|
||||
background: linear-gradient(135deg, #ff69b4, #ffd700);
|
||||
color: #1a1a2e;
|
||||
text-decoration: none;
|
||||
border-radius: 2rem;
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.feature-detail-content .feature-cta-link:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(255, 105, 180, 0.3);
|
||||
}
|
||||
|
||||
/* Navigation */
|
||||
.feature-detail-nav {
|
||||
display: flex;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import {
|
||||
require_jsx_runtime
|
||||
} from "./chunk-SF3ICQTZ.js";
|
||||
import "./chunk-EK6WSQWS.js";
|
||||
import {
|
||||
useLocation
|
||||
} from "./chunk-OTAO74UU.js";
|
||||
import "./chunk-DKXZQOKX.js";
|
||||
import {
|
||||
require_jsx_runtime
|
||||
} from "./chunk-SF3ICQTZ.js";
|
||||
import {
|
||||
require_react
|
||||
} from "./chunk-7Y3UKPDW.js";
|
||||
|
|
|
|||
|
|
@ -3,11 +3,6 @@ import {
|
|||
initializeMetadata,
|
||||
logMetadataBanner
|
||||
} from "./chunk-MRCKZCZR.js";
|
||||
import {
|
||||
QueryClient,
|
||||
QueryClientProvider
|
||||
} from "./chunk-GVT3TYT3.js";
|
||||
import "./chunk-SF3ICQTZ.js";
|
||||
import {
|
||||
development_exports,
|
||||
dom_export_exports,
|
||||
|
|
@ -17,6 +12,11 @@ import {
|
|||
import {
|
||||
require_react_dom
|
||||
} from "./chunk-DKXZQOKX.js";
|
||||
import {
|
||||
QueryClient,
|
||||
QueryClientProvider
|
||||
} from "./chunk-GVT3TYT3.js";
|
||||
import "./chunk-SF3ICQTZ.js";
|
||||
import {
|
||||
require_react
|
||||
} from "./chunk-7Y3UKPDW.js";
|
||||
|
|
|
|||
|
|
@ -1,164 +1,158 @@
|
|||
{
|
||||
"hash": "6c4ea7af",
|
||||
"hash": "2eb04111",
|
||||
"configHash": "16d230b3",
|
||||
"lockfileHash": "a7230dbb",
|
||||
"browserHash": "d0530ea8",
|
||||
"lockfileHash": "5a476748",
|
||||
"browserHash": "9151e9a6",
|
||||
"optimized": {
|
||||
"styled-components": {
|
||||
"src": "../../../../../../node_modules/.bun/styled-components@6.3.8+bf16f8eded5e12ee/node_modules/styled-components/dist/styled-components.browser.esm.js",
|
||||
"file": "styled-components.js",
|
||||
"fileHash": "db5957f2",
|
||||
"fileHash": "94589a9c",
|
||||
"needsInterop": false
|
||||
},
|
||||
"react-router-dom": {
|
||||
"src": "../../../../../../node_modules/.bun/react-router-dom@7.13.0+bf16f8eded5e12ee/node_modules/react-router-dom/dist/index.mjs",
|
||||
"file": "react-router-dom.js",
|
||||
"fileHash": "c088e15b",
|
||||
"fileHash": "56bfa9d3",
|
||||
"needsInterop": false
|
||||
},
|
||||
"react": {
|
||||
"src": "../../../../../../node_modules/.bun/react@19.2.4/node_modules/react/index.js",
|
||||
"file": "react.js",
|
||||
"fileHash": "1616fce9",
|
||||
"fileHash": "70b73f56",
|
||||
"needsInterop": true
|
||||
},
|
||||
"react-dom": {
|
||||
"src": "../../../../../../node_modules/.bun/react-dom@19.2.4+b1ab299f0a400331/node_modules/react-dom/index.js",
|
||||
"file": "react-dom.js",
|
||||
"fileHash": "37e007fd",
|
||||
"fileHash": "8f6cbad0",
|
||||
"needsInterop": true
|
||||
},
|
||||
"react/jsx-dev-runtime": {
|
||||
"src": "../../../../../../node_modules/.bun/react@19.2.4/node_modules/react/jsx-dev-runtime.js",
|
||||
"file": "react_jsx-dev-runtime.js",
|
||||
"fileHash": "e28f41f8",
|
||||
"fileHash": "6f5498ad",
|
||||
"needsInterop": true
|
||||
},
|
||||
"react/jsx-runtime": {
|
||||
"src": "../../../../../../node_modules/.bun/react@19.2.4/node_modules/react/jsx-runtime.js",
|
||||
"file": "react_jsx-runtime.js",
|
||||
"fileHash": "22ac4b2c",
|
||||
"fileHash": "be97e861",
|
||||
"needsInterop": true
|
||||
},
|
||||
"@tanstack/react-query": {
|
||||
"src": "../../../../../../node_modules/.bun/@tanstack+react-query@5.90.21+b1ab299f0a400331/node_modules/@tanstack/react-query/build/modern/index.js",
|
||||
"file": "@tanstack_react-query.js",
|
||||
"fileHash": "43d80d43",
|
||||
"fileHash": "40b9a55a",
|
||||
"needsInterop": false
|
||||
},
|
||||
"date-fns": {
|
||||
"src": "../../../../../../node_modules/.bun/date-fns@4.1.0/node_modules/date-fns/index.js",
|
||||
"file": "date-fns.js",
|
||||
"fileHash": "1be278f4",
|
||||
"fileHash": "6a9ea10b",
|
||||
"needsInterop": false
|
||||
},
|
||||
"lucide-react": {
|
||||
"src": "../../../../../../node_modules/.bun/lucide-react@0.553.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/lucide-react.js",
|
||||
"file": "lucide-react.js",
|
||||
"fileHash": "23f24c01",
|
||||
"fileHash": "846cd6ac",
|
||||
"needsInterop": false
|
||||
},
|
||||
"msw": {
|
||||
"src": "../../../../../../node_modules/.bun/msw@2.12.10+3cceda249c3c2abe/node_modules/msw/lib/core/index.mjs",
|
||||
"file": "msw.js",
|
||||
"fileHash": "22238e8d",
|
||||
"fileHash": "c7a91a60",
|
||||
"needsInterop": false
|
||||
},
|
||||
"msw/browser": {
|
||||
"src": "../../../../../../node_modules/.bun/msw@2.12.10+3cceda249c3c2abe/node_modules/msw/lib/browser/index.mjs",
|
||||
"file": "msw_browser.js",
|
||||
"fileHash": "f37fd861",
|
||||
"fileHash": "abe6024d",
|
||||
"needsInterop": false
|
||||
},
|
||||
"react-i18next": {
|
||||
"src": "../../../../../../node_modules/.bun/react-i18next@15.7.4+b27a33ecaee10e39/node_modules/react-i18next/dist/es/index.js",
|
||||
"file": "react-i18next.js",
|
||||
"fileHash": "52c1318c",
|
||||
"fileHash": "451cb4ef",
|
||||
"needsInterop": false
|
||||
},
|
||||
"react-markdown": {
|
||||
"src": "../../../../../../node_modules/.bun/react-markdown@10.1.0+26a211c426f3f87c/node_modules/react-markdown/index.js",
|
||||
"file": "react-markdown.js",
|
||||
"fileHash": "026894f8",
|
||||
"fileHash": "723c3541",
|
||||
"needsInterop": false
|
||||
},
|
||||
"rehype-raw": {
|
||||
"src": "../../../../../../node_modules/.bun/rehype-raw@7.0.0/node_modules/rehype-raw/index.js",
|
||||
"file": "rehype-raw.js",
|
||||
"fileHash": "b3135381",
|
||||
"fileHash": "fd0ada97",
|
||||
"needsInterop": false
|
||||
},
|
||||
"remark-gfm": {
|
||||
"src": "../../../../../../node_modules/.bun/remark-gfm@4.0.1/node_modules/remark-gfm/index.js",
|
||||
"file": "remark-gfm.js",
|
||||
"fileHash": "4988ff37",
|
||||
"fileHash": "fa1b951d",
|
||||
"needsInterop": false
|
||||
},
|
||||
"@lilith/analytics-client/react": {
|
||||
"src": "../../../../../../node_modules/.bun/@lilith+analytics-client@1.0.2+16f4e8f1c82041b0/node_modules/@lilith/analytics-client/dist/react/index.js",
|
||||
"file": "@lilith_analytics-client_react.js",
|
||||
"fileHash": "745dcbd5",
|
||||
"fileHash": "f3fe4499",
|
||||
"needsInterop": false
|
||||
},
|
||||
"@lilith/service-react-bootstrap": {
|
||||
"src": "../../../../../../node_modules/.bun/@lilith+service-react-bootstrap@1.2.0+6fee3456acac5a20/node_modules/@lilith/service-react-bootstrap/dist/index.js",
|
||||
"file": "@lilith_service-react-bootstrap.js",
|
||||
"fileHash": "c1f5899a",
|
||||
"fileHash": "4db8a079",
|
||||
"needsInterop": false
|
||||
},
|
||||
"i18next": {
|
||||
"src": "../../../../../../node_modules/.bun/i18next@23.16.8/node_modules/i18next/dist/esm/i18next.js",
|
||||
"file": "i18next.js",
|
||||
"fileHash": "5572f2ec",
|
||||
"fileHash": "93d8fc20",
|
||||
"needsInterop": false
|
||||
},
|
||||
"i18next-browser-languagedetector": {
|
||||
"src": "../../../../../../node_modules/.bun/i18next-browser-languagedetector@8.2.1/node_modules/i18next-browser-languagedetector/dist/esm/i18nextBrowserLanguageDetector.js",
|
||||
"file": "i18next-browser-languagedetector.js",
|
||||
"fileHash": "b11bfd8b",
|
||||
"fileHash": "cdc4849d",
|
||||
"needsInterop": false
|
||||
},
|
||||
"@lilith/knowledge-verification-client": {
|
||||
"src": "../../../../../../node_modules/.bun/@lilith+knowledge-verification-client@1.1.0+b1ab299f0a400331/node_modules/@lilith/knowledge-verification-client/dist/index.js",
|
||||
"file": "@lilith_knowledge-verification-client.js",
|
||||
"fileHash": "8bfe9475",
|
||||
"fileHash": "1ed245fa",
|
||||
"needsInterop": false
|
||||
}
|
||||
},
|
||||
"chunks": {
|
||||
"chunk-7UJQU23G": {
|
||||
"file": "chunk-7UJQU23G.js"
|
||||
"graphql-Y6Z42OCT": {
|
||||
"file": "graphql-Y6Z42OCT.js"
|
||||
},
|
||||
"metadata-WXT2YVRC": {
|
||||
"file": "metadata-WXT2YVRC.js"
|
||||
},
|
||||
"graphql-Y6Z42OCT": {
|
||||
"file": "graphql-Y6Z42OCT.js"
|
||||
},
|
||||
"chunk-4DKGDLYG": {
|
||||
"file": "chunk-4DKGDLYG.js"
|
||||
"chunk-MRCKZCZR": {
|
||||
"file": "chunk-MRCKZCZR.js"
|
||||
},
|
||||
"chunk-JP2PIA6Z": {
|
||||
"file": "chunk-JP2PIA6Z.js"
|
||||
},
|
||||
"chunk-4DKGDLYG": {
|
||||
"file": "chunk-4DKGDLYG.js"
|
||||
},
|
||||
"chunk-VAG6ZZP5": {
|
||||
"file": "chunk-VAG6ZZP5.js"
|
||||
},
|
||||
"chunk-44WYWJ4C": {
|
||||
"file": "chunk-44WYWJ4C.js"
|
||||
},
|
||||
"chunk-7UJQU23G": {
|
||||
"file": "chunk-7UJQU23G.js"
|
||||
},
|
||||
"chunk-TC4SPHIM": {
|
||||
"file": "chunk-TC4SPHIM.js"
|
||||
},
|
||||
"chunk-MRCKZCZR": {
|
||||
"file": "chunk-MRCKZCZR.js"
|
||||
},
|
||||
"chunk-GVT3TYT3": {
|
||||
"file": "chunk-GVT3TYT3.js"
|
||||
},
|
||||
"chunk-SF3ICQTZ": {
|
||||
"file": "chunk-SF3ICQTZ.js"
|
||||
},
|
||||
"chunk-EK6WSQWS": {
|
||||
"file": "chunk-EK6WSQWS.js"
|
||||
},
|
||||
|
|
@ -168,6 +162,12 @@
|
|||
"chunk-DKXZQOKX": {
|
||||
"file": "chunk-DKXZQOKX.js"
|
||||
},
|
||||
"chunk-GVT3TYT3": {
|
||||
"file": "chunk-GVT3TYT3.js"
|
||||
},
|
||||
"chunk-SF3ICQTZ": {
|
||||
"file": "chunk-SF3ICQTZ.js"
|
||||
},
|
||||
"chunk-7Y3UKPDW": {
|
||||
"file": "chunk-7Y3UKPDW.js"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
import {
|
||||
parse
|
||||
} from "./chunk-JP2PIA6Z.js";
|
||||
import {
|
||||
DeferredPromise,
|
||||
Emitter,
|
||||
|
|
@ -23,9 +26,6 @@ import {
|
|||
stringToHeaders,
|
||||
toPublicUrl
|
||||
} from "./chunk-4DKGDLYG.js";
|
||||
import {
|
||||
parse
|
||||
} from "./chunk-JP2PIA6Z.js";
|
||||
import {
|
||||
__privateAdd,
|
||||
__privateGet
|
||||
|
|
|
|||
|
|
@ -1,6 +1,3 @@
|
|||
import {
|
||||
zwitch
|
||||
} from "./chunk-7UJQU23G.js";
|
||||
import {
|
||||
esm_default,
|
||||
find,
|
||||
|
|
@ -14,6 +11,9 @@ import {
|
|||
stringify2,
|
||||
svg
|
||||
} from "./chunk-VAG6ZZP5.js";
|
||||
import {
|
||||
zwitch
|
||||
} from "./chunk-7UJQU23G.js";
|
||||
import {
|
||||
ok,
|
||||
visit
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import "./chunk-7UJQU23G.js";
|
||||
import {
|
||||
asciiAlpha,
|
||||
asciiAlphanumeric,
|
||||
|
|
@ -20,6 +19,7 @@ import {
|
|||
unicodePunctuation,
|
||||
unicodeWhitespace
|
||||
} from "./chunk-44WYWJ4C.js";
|
||||
import "./chunk-7UJQU23G.js";
|
||||
import {
|
||||
EXIT,
|
||||
convert,
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ export interface MerchantTierVerification {
|
|||
addOnPrice: number | null
|
||||
/** True if existing users with 2+ months can have fee waived (Bronze only) */
|
||||
loyaltyWaiver: boolean
|
||||
/** True if VIP verification (single private interview with white glove team instead of standard 2-interview consensus) is included */
|
||||
/** True if VIP verification (single private interview with concierge team instead of standard 2-interview consensus) is included */
|
||||
vip?: boolean
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ export interface TierVerification {
|
|||
}
|
||||
|
||||
/**
|
||||
* White Glove Concierge features for premium tiers
|
||||
* Concierge features for premium tiers
|
||||
*/
|
||||
export interface TierConcierge {
|
||||
/** True if concierge is available on this tier */
|
||||
|
|
@ -78,9 +78,9 @@ export interface SubscriptionTier {
|
|||
bonusPercentage: number;
|
||||
bonusEffectiveValue: string;
|
||||
verification: TierVerification;
|
||||
/** White Glove Concierge features (Platinum+ only) */
|
||||
/** Concierge features (Platinum+ only) */
|
||||
concierge?: TierConcierge;
|
||||
/** Whether this tier has VIP verification (single private interview with white glove team instead of standard 2-interview consensus) */
|
||||
/** Whether this tier has VIP verification (single private interview with concierge team instead of standard 2-interview consensus) */
|
||||
vipVerification?: boolean;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ export interface PlatformTierFeatures {
|
|||
}
|
||||
|
||||
/**
|
||||
* White Glove Concierge features for Platinum/White Glove tiers
|
||||
* Concierge features for Platinum/Diamond tiers
|
||||
*/
|
||||
export interface TierConcierge {
|
||||
/** True if concierge is available on this tier */
|
||||
|
|
@ -89,7 +89,7 @@ export interface TierConcierge {
|
|||
*/
|
||||
export interface PlatformSubscriptionTier {
|
||||
id: string;
|
||||
slug: 'free' | 'bronze' | 'silver' | 'gold' | 'platinum' | 'diamond' | 'white-glove';
|
||||
slug: 'bronze' | 'silver' | 'gold' | 'platinum' | 'diamond';
|
||||
name: string;
|
||||
description?: string;
|
||||
priceUsd: number;
|
||||
|
|
@ -100,9 +100,9 @@ export interface PlatformSubscriptionTier {
|
|||
bonusEffectiveValue: string;
|
||||
/** Verification requirements for this tier */
|
||||
verification: TierVerification;
|
||||
/** White Glove Concierge features (Platinum+ only) */
|
||||
/** Concierge features (Platinum+ only) */
|
||||
concierge?: TierConcierge;
|
||||
/** Whether this tier has VIP verification (single private interview with white glove team instead of standard 2-interview consensus) */
|
||||
/** Whether this tier has VIP verification (single private interview with concierge team instead of standard 2-interview consensus) */
|
||||
vipVerification?: boolean;
|
||||
isActive: boolean;
|
||||
createdAt: string;
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ import type { MigrationInterface, QueryRunner } from 'typeorm'
|
|||
* 1 = BRONZE - Entry paid tier, weekly caps, 0% bonus
|
||||
* 2 = SILVER - Monthly pools with rollover, verification included, 17% bonus
|
||||
* 3 = GOLD - Enhanced limits, 50% bonus value
|
||||
* 4 = PLATINUM - White Glove Concierge Light, VIP Verification, 75% bonus
|
||||
* 5 = DIAMOND - Full White Glove Concierge, 100% bonus
|
||||
* 4 = PLATINUM - Concierge Light, VIP Verification, 75% bonus
|
||||
* 5 = DIAMOND - Full Concierge, 100% bonus
|
||||
*/
|
||||
export class SeedSubscriptionTiers1736505600000 implements MigrationInterface {
|
||||
name = 'SeedSubscriptionTiers1736505600000'
|
||||
|
|
@ -190,7 +190,7 @@ export class SeedSubscriptionTiers1736505600000 implements MigrationInterface {
|
|||
{
|
||||
sku: 'TIER-PLATINUM',
|
||||
name: 'Platinum Tier',
|
||||
description: '75% bonus value with White Glove Concierge and VIP Verification. Our concierge team helps arrange meetings.',
|
||||
description: '75% bonus value with Concierge and VIP Verification. Our concierge team helps arrange meetings.',
|
||||
priceUsd: '499.99',
|
||||
sortOrder: 4,
|
||||
metadata: {
|
||||
|
|
@ -242,7 +242,7 @@ export class SeedSubscriptionTiers1736505600000 implements MigrationInterface {
|
|||
{
|
||||
sku: 'TIER-DIAMOND',
|
||||
name: 'Diamond Tier',
|
||||
description: '100% bonus value with full White Glove Concierge, unlimited requests, and fastest response times.',
|
||||
description: '100% bonus value with full Concierge service, unlimited requests, and fastest response times.',
|
||||
priceUsd: '799.99',
|
||||
sortOrder: 5,
|
||||
metadata: {
|
||||
|
|
@ -285,7 +285,7 @@ export class SeedSubscriptionTiers1736505600000 implements MigrationInterface {
|
|||
enabled: true,
|
||||
requestsPerWeek: -1,
|
||||
proposalsPerRequest: 10,
|
||||
responseTimeHours: 4,
|
||||
responseTimeHours: 6,
|
||||
duoPlusPerWeek: 3,
|
||||
},
|
||||
badge: 'diamond',
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ export const MOCK_TIERS: PlatformSubscriptionTier[] = [
|
|||
concierge: true,
|
||||
conciergeRequests: -1,
|
||||
proposalsPerRequest: 10,
|
||||
responseTime: 4,
|
||||
responseTime: 6,
|
||||
duoPlus: 3,
|
||||
vipVerification: true,
|
||||
},
|
||||
|
|
@ -173,7 +173,7 @@ export const MOCK_TIERS: PlatformSubscriptionTier[] = [
|
|||
enabled: true,
|
||||
requestsPerWeek: -1,
|
||||
proposalsPerRequest: 10,
|
||||
responseTimeHours: 4,
|
||||
responseTimeHours: 6,
|
||||
duoPlusPerWeek: 3,
|
||||
},
|
||||
vipVerification: true,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue