Skip to content

Commit a92288e

Browse files
authored
Merge pull request #43 from Agbasimere/main
feat(web): scaffold next.js app-router with shadcn-style nav and zust…
2 parents 950a1e8 + 9007b8e commit a92288e

53 files changed

Lines changed: 16753 additions & 2106 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/CI.yml

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,13 @@ jobs:
6666
with:
6767
node-version: 20
6868
cache: 'npm'
69-
cache-dependency-path: apps/web/package-lock.json
69+
cache-dependency-path: package-lock.json
7070
- name: Install dependencies
71-
run: npm install --prefix apps/web
71+
run: npm ci
7272
- name: Lint
73-
run: npm run lint --prefix apps/web
73+
run: npm run lint -w web
7474
- name: Build
75-
run: npm run build --prefix apps/web
75+
run: npm run build -w web
7676

7777
playwright-e2e:
7878
name: E2E Tests (Playwright)
@@ -85,10 +85,8 @@ jobs:
8585
node-version: 20
8686
cache: 'npm'
8787
cache-dependency-path: package-lock.json
88-
- name: Install root dependencies
89-
run: npm install
90-
- name: Install web dependencies
91-
run: npm install --prefix apps/web
88+
- name: Install dependencies
89+
run: npm ci
9290
- name: Install Playwright Browsers
9391
run: npx playwright install --with-deps chromium
9492
- name: Run E2E tests

.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
legacy-peer-deps=true

Cargo.lock

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/web/app/globals.css

Lines changed: 121 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,119 @@
11
@import "tailwindcss";
22

33
:root {
4-
--background: #f8f5ef;
5-
--foreground: #0f172a;
6-
--font-sans-stack: "Avenir Next", "Segoe UI", sans-serif;
4+
--background: #f7f4ed;
5+
--foreground: #112032;
6+
--card: rgba(255, 250, 243, 0.9);
7+
--card-foreground: #112032;
8+
--popover: rgba(255, 250, 243, 0.98);
9+
--popover-foreground: #112032;
10+
--primary: #0d7c66;
11+
--primary-foreground: #effcf8;
12+
--secondary: #e7dfd0;
13+
--secondary-foreground: #112032;
14+
--muted: #efe7db;
15+
--muted-foreground: #536273;
16+
--accent: #e3f6f1;
17+
--accent-foreground: #0f4f43;
18+
--destructive: #c65353;
19+
--destructive-foreground: #fff8f7;
20+
--border: rgba(17, 32, 50, 0.12);
21+
--input: rgba(17, 32, 50, 0.16);
22+
--ring: rgba(13, 124, 102, 0.42);
23+
--chart-1: #0d7c66;
24+
--chart-2: #e29a2f;
25+
--chart-3: #173b63;
26+
--chart-4: #58b89b;
27+
--chart-5: #9d6132;
28+
--radius: 1.25rem;
29+
--font-sans-stack: "Sora", "Avenir Next", "Segoe UI", sans-serif;
730
--font-mono-stack: "SFMono-Regular", "SF Mono", "Roboto Mono", monospace;
831
}
932

1033
@theme inline {
1134
--color-background: var(--background);
1235
--color-foreground: var(--foreground);
36+
--color-card: var(--card);
37+
--color-card-foreground: var(--card-foreground);
38+
--color-popover: var(--popover);
39+
--color-popover-foreground: var(--popover-foreground);
40+
--color-primary: var(--primary);
41+
--color-primary-foreground: var(--primary-foreground);
42+
--color-secondary: var(--secondary);
43+
--color-secondary-foreground: var(--secondary-foreground);
44+
--color-muted: var(--muted);
45+
--color-muted-foreground: var(--muted-foreground);
46+
--color-accent: var(--accent);
47+
--color-accent-foreground: var(--accent-foreground);
48+
--color-destructive: var(--destructive);
49+
--color-destructive-foreground: var(--destructive-foreground);
50+
--color-border: var(--border);
51+
--color-input: var(--input);
52+
--color-ring: var(--ring);
53+
--radius-sm: calc(var(--radius) - 8px);
54+
--radius-md: calc(var(--radius) - 4px);
55+
--radius-lg: var(--radius);
56+
--radius-xl: calc(var(--radius) + 4px);
1357
--font-sans: var(--font-sans-stack);
1458
--font-mono: var(--font-mono-stack);
1559
}
1660

17-
@media (prefers-color-scheme: dark) {
18-
:root {
19-
--background: #0f172a;
20-
--foreground: #f8fafc;
21-
}
61+
.dark {
62+
--background: #09131f;
63+
--foreground: #edf6f7;
64+
--card: rgba(10, 21, 34, 0.84);
65+
--card-foreground: #edf6f7;
66+
--popover: rgba(9, 19, 31, 0.96);
67+
--popover-foreground: #edf6f7;
68+
--primary: #45c1a2;
69+
--primary-foreground: #052c24;
70+
--secondary: rgba(89, 111, 131, 0.22);
71+
--secondary-foreground: #edf6f7;
72+
--muted: rgba(89, 111, 131, 0.2);
73+
--muted-foreground: #98a8b8;
74+
--accent: rgba(69, 193, 162, 0.14);
75+
--accent-foreground: #d8fbf2;
76+
--destructive: #f28585;
77+
--destructive-foreground: #20090a;
78+
--border: rgba(237, 246, 247, 0.1);
79+
--input: rgba(237, 246, 247, 0.12);
80+
--ring: rgba(69, 193, 162, 0.3);
81+
--chart-1: #45c1a2;
82+
--chart-2: #f4b860;
83+
--chart-3: #87aef2;
84+
--chart-4: #4f8b7a;
85+
--chart-5: #ff9770;
2286
}
2387

24-
* {
25-
box-sizing: border-box;
26-
}
88+
@layer base {
89+
* {
90+
@apply border-border;
91+
}
92+
93+
html {
94+
scroll-behavior: smooth;
95+
}
96+
97+
body {
98+
@apply bg-background text-foreground;
99+
min-height: 100vh;
100+
font-family: var(--font-sans-stack);
101+
background-image:
102+
radial-gradient(circle at top left, rgba(226, 154, 47, 0.18), transparent 24%),
103+
radial-gradient(circle at 90% 18%, rgba(13, 124, 102, 0.14), transparent 22%),
104+
linear-gradient(180deg, rgba(247, 244, 237, 0.98), rgba(240, 245, 247, 0.95));
105+
}
27106

28-
html {
29-
scroll-behavior: smooth;
107+
.dark body {
108+
background-image:
109+
radial-gradient(circle at top left, rgba(242, 183, 90, 0.12), transparent 20%),
110+
radial-gradient(circle at 88% 16%, rgba(69, 193, 162, 0.16), transparent 22%),
111+
linear-gradient(180deg, rgba(9, 19, 31, 0.98), rgba(8, 17, 29, 0.96));
112+
}
30113
}
31114

32-
body {
33-
background: var(--background);
34-
color: var(--foreground);
35-
font-family: var(--font-sans-stack);
115+
* {
116+
box-sizing: border-box;
36117
}
37118

38119
a {
@@ -41,5 +122,27 @@ a {
41122
}
42123

43124
::selection {
44-
background: rgba(245, 158, 11, 0.28);
125+
background: rgba(69, 193, 162, 0.28);
126+
}
127+
128+
.glass-surface {
129+
background: color-mix(in srgb, var(--card) 82%, transparent);
130+
backdrop-filter: blur(18px);
131+
}
132+
133+
.noise-overlay {
134+
position: relative;
135+
}
136+
137+
.noise-overlay::before {
138+
content: "";
139+
position: absolute;
140+
inset: 0;
141+
pointer-events: none;
142+
background-image:
143+
linear-gradient(rgba(255, 255, 255, 0.024) 1px, transparent 1px),
144+
linear-gradient(90deg, rgba(255, 255, 255, 0.024) 1px, transparent 1px);
145+
background-size: 24px 24px;
146+
mask-image: linear-gradient(180deg, rgba(0, 0, 0, 0.9), transparent);
147+
opacity: 0.35;
45148
}

apps/web/app/jobs/[id]/page.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,11 @@ export default function JobDetailsPage() {
7272
setBusyAction(`accept-${bidId}`);
7373

7474
try {
75-
await api.bids.accept(id, bidId, {
75+
const acceptedJob = await api.bids.accept(id, bidId, {
7676
client_address: workspace.job.client_address,
7777
});
78-
await workspace.refresh();
79-
router.push(`/jobs/${id}/fund`);
78+
void workspace.refresh();
79+
router.push(`/jobs/${acceptedJob.id}/fund`);
8080
} catch {
8181
alert("Failed to accept bid");
8282
} finally {

apps/web/app/layout.tsx

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import type { Metadata } from "next";
22
import "./globals.css";
3+
import { DashboardLayout } from "@/components/layout/dashboard-layout";
4+
import { Providers } from "@/components/providers";
35
import { ToastProvider } from "@/components/ui/toast-provider";
46

57
export const metadata: Metadata = {
6-
title: "Lance - Decentralized Freelance Marketplace",
7-
description: "Stellar-native freelance marketplace with AI-powered dispute resolution",
8+
title: "Lance | Soroban Freelance Intelligence",
9+
description:
10+
"Soroban-native freelance operations with escrow, reputation, and dispute intelligence.",
811
};
912

1013
export default function RootLayout({
@@ -13,10 +16,15 @@ export default function RootLayout({
1316
children: React.ReactNode;
1417
}>) {
1518
return (
16-
<html lang="en">
17-
<body className="antialiased">
18-
<ToastProvider>{children}</ToastProvider>
19+
<html lang="en" suppressHydrationWarning>
20+
<body className="bg-background text-foreground antialiased">
21+
<Providers>
22+
<ToastProvider>
23+
<DashboardLayout>{children}</DashboardLayout>
24+
</ToastProvider>
25+
</Providers>
1926
</body>
2027
</html>
2128
);
2229
}
30+

apps/web/app/page.tsx

Lines changed: 2 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,5 @@
1-
import Link from "next/link";
2-
import { ArrowRight, BriefcaseBusiness, Gavel, ShieldCheck, Star } from "lucide-react";
31
import { SiteShell } from "@/components/site-shell";
4-
5-
const highlights = [
6-
{
7-
title: "Trustless Profiles",
8-
description:
9-
"Blend editable bios and portfolio links with Soroban reputation math so serious freelancers can market verified credibility everywhere.",
10-
href: "/profile/GD...CLIENT",
11-
icon: Star,
12-
},
13-
{
14-
title: "Live Job Workspaces",
15-
description:
16-
"Keep both sides aligned around milestones, evidence, escrow state, and payout actions inside a single shared dashboard.",
17-
href: "/jobs",
18-
icon: BriefcaseBusiness,
19-
},
20-
{
21-
title: "Neutral Dispute Center",
22-
description:
23-
"Explain evidence, AI reasoning, and final payout splits with courtroom-level clarity once cooperation breaks down.",
24-
href: "/jobs",
25-
icon: Gavel,
26-
},
27-
];
2+
import { RoleOverview } from "@/components/dashboard/role-overview";
283

294
export default function Home() {
305
return (
@@ -33,89 +8,7 @@ export default function Home() {
338
title="Premium freelance execution with escrow, verifiable reputation, and transparent AI arbitration."
349
description="Lance is the surface layer for serious clients and elite independents who want payment security, immutable trust signals, and fast dispute resolution without losing clarity."
3510
>
36-
<div className="grid gap-6 lg:grid-cols-[1.4fr_0.9fr]">
37-
<section className="rounded-[2rem] border border-slate-200 bg-white/85 p-8 shadow-[0_30px_80px_-48px_rgba(15,23,42,0.55)] sm:p-10">
38-
<div className="flex flex-col gap-6">
39-
<div className="inline-flex w-fit items-center gap-2 rounded-full border border-amber-200 bg-amber-50 px-4 py-2 text-xs font-semibold uppercase tracking-[0.24em] text-amber-800">
40-
Trust by design
41-
</div>
42-
<h2 className="max-w-2xl text-3xl font-semibold tracking-tight text-slate-950 sm:text-4xl">
43-
Every page is built to make strong operators look stronger.
44-
</h2>
45-
<p className="max-w-2xl text-base leading-7 text-slate-600">
46-
Public profiles become acquisition funnels, active jobs become
47-
command centers, and disputes become legible instead of chaotic.
48-
</p>
49-
<div className="flex flex-col gap-3 sm:flex-row">
50-
<Link
51-
href="/jobs"
52-
className="inline-flex items-center justify-center gap-2 rounded-full bg-slate-950 px-6 py-3 text-sm font-semibold text-white transition hover:bg-slate-800"
53-
>
54-
Explore Job Board
55-
<ArrowRight className="h-4 w-4" />
56-
</Link>
57-
<Link
58-
href="/jobs/new"
59-
className="inline-flex items-center justify-center rounded-full border border-slate-200 px-6 py-3 text-sm font-semibold text-slate-700 transition hover:border-amber-300 hover:text-slate-950"
60-
>
61-
Post a Job
62-
</Link>
63-
</div>
64-
</div>
65-
</section>
66-
67-
<aside className="rounded-[2rem] border border-slate-200/80 bg-slate-950 p-8 text-slate-50 shadow-[0_30px_80px_-48px_rgba(15,23,42,0.8)]">
68-
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-amber-300">
69-
Release posture
70-
</p>
71-
<div className="mt-6 space-y-5">
72-
<div>
73-
<p className="text-4xl font-semibold">4</p>
74-
<p className="mt-1 text-sm text-slate-300">
75-
Core surfaces now aligned: profiles, marketplace, job overview,
76-
and dispute resolution.
77-
</p>
78-
</div>
79-
<div className="rounded-3xl border border-white/10 bg-white/5 p-5">
80-
<div className="flex items-center gap-3">
81-
<ShieldCheck className="h-5 w-5 text-amber-300" />
82-
<p className="text-sm font-medium">Escrow-first workflow</p>
83-
</div>
84-
<p className="mt-3 text-sm leading-6 text-slate-300">
85-
Fund milestones, upload proof, approve releases, or escalate
86-
into a locked dispute flow with on-chain receipts.
87-
</p>
88-
</div>
89-
</div>
90-
</aside>
91-
</div>
92-
93-
<section className="mt-10 grid gap-5 lg:grid-cols-3">
94-
{highlights.map((item) => {
95-
const Icon = item.icon;
96-
return (
97-
<Link
98-
key={item.title}
99-
href={item.href}
100-
className="group rounded-[1.75rem] border border-slate-200 bg-white/80 p-6 transition hover:-translate-y-1 hover:border-amber-300 hover:shadow-[0_25px_60px_-40px_rgba(15,23,42,0.45)]"
101-
>
102-
<div className="flex h-12 w-12 items-center justify-center rounded-2xl bg-amber-100 text-amber-700">
103-
<Icon className="h-5 w-5" />
104-
</div>
105-
<h3 className="mt-5 text-xl font-semibold text-slate-950">
106-
{item.title}
107-
</h3>
108-
<p className="mt-3 text-sm leading-6 text-slate-600">
109-
{item.description}
110-
</p>
111-
<span className="mt-5 inline-flex items-center gap-2 text-sm font-semibold text-slate-950">
112-
Open surface
113-
<ArrowRight className="h-4 w-4 transition group-hover:translate-x-1" />
114-
</span>
115-
</Link>
116-
);
117-
})}
118-
</section>
11+
<RoleOverview />
11912
</SiteShell>
12013
);
12114
}

0 commit comments

Comments
 (0)