Skip to content

Commit 8f91dd7

Browse files
committed
2 parents bfd6769 + 7211c49 commit 8f91dd7

37 files changed

Lines changed: 1871 additions & 507 deletions

apps/api-runner/node_modules/.bin/tsc

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

apps/api-runner/node_modules/.bin/tsserver

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

apps/backend/app/api/routes/auth/api.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,9 @@ async def update_profile(
257257
if payload.personal_info is not None:
258258
updates["personal_info"] = payload.personal_info.model_dump()
259259

260+
if payload.persona is not None:
261+
updates["persona"] = payload.persona
262+
260263
if updates:
261264
await update_user_profile(current_user.uid, updates)
262265

apps/backend/app/api/routes/auth/schema.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ class UserProfileResponse(BaseModel):
108108
certifications: list[Certification] = Field(default_factory=list)
109109
portfolio_settings: PortfolioSettings | None = None
110110
personal_info: PersonalInfo | None = None
111+
# Role picked during onboarding (frontend/backend/fullstack/devops/qa/designer/exploring)
112+
persona: str | None = None
111113
onboarding_completed: bool = False
112114
# Workspace migration fields (T25)
113115
workspace_setup_at: int | None = None
@@ -128,6 +130,7 @@ class UpdateProfileRequest(BaseModel):
128130
certifications: list[Certification] | None = None
129131
portfolio_settings: PortfolioSettings | None = None
130132
personal_info: PersonalInfo | None = None
133+
persona: str | None = None
131134

132135

133136
class OkResponse(BaseModel):

apps/web/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"@radix-ui/react-toggle": "^1.1.10",
4343
"@radix-ui/react-toggle-group": "^1.1.11",
4444
"@radix-ui/react-tooltip": "^1.2.8",
45+
"@radix-ui/react-visually-hidden": "^1.2.7",
4546
"@simplewebauthn/browser": "^11.0.0",
4647
"@space-man/react-theme-animation": "^1.1.1",
4748
"@tabler/icons-react": "^3.35.0",

apps/web/src/app/app/api-keys/page.tsx

Lines changed: 10 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { useEffect, useRef } from "react"
44
import { AddApiKeyDialog } from "@/components/api-key-vault/add-api-key-dialog"
55
import { ApiKeyList } from "@/components/api-key-vault/api-key-list"
66
import { useApiKeyVaultStore, type ApiKeyEntry, type ApiKeyEnv } from "@/store/api-key-vault-store"
7-
import { ShieldCheck } from "lucide-react"
7+
import { ToolHeader } from "@/components/tools/tool-header"
88
import { useVaultGuard } from "@/hooks/use-vault-guard"
99
import { VaultLockedPlaceholder } from "@/components/vault-locked-placeholder"
1010
import { VaultRestoringSkeleton } from "@/components/vault-restoring-skeleton"
@@ -44,7 +44,7 @@ export default function ApiKeyVaultPage() {
4444
const { user, loading } = useAuth(true)
4545
const encryptionKey = useCipherKey()
4646
const { isUnlocked, isRestoring } = useVaultGuard()
47-
const { entries, setEntries, setLoading, clearEntries } = useApiKeyVaultStore()
47+
const { setEntries, setLoading, clearEntries } = useApiKeyVaultStore()
4848
const isMobile = useIsMobile()
4949
const loadedRef = useRef(false)
5050

@@ -130,39 +130,19 @@ export default function ApiKeyVaultPage() {
130130
: "h-full flex flex-col container mx-auto px-4 md:px-6 lg:px-8 min-h-0"
131131
}
132132
>
133+
<ToolHeader
134+
title="API Keys"
135+
description="AES-256-GCM encrypted on your device — server never sees plaintext."
136+
toolId="/app/api-keys"
137+
className="shrink-0"
138+
/>
139+
133140
{!isMobile && (
134-
<div className="flex justify-between items-end py-6 shrink-0 gap-4">
135-
<div className="min-w-0">
136-
<div className="flex items-center gap-3">
137-
<h1 className="text-3xl font-bold tracking-tight">API Keys</h1>
138-
{entries.length > 0 && (
139-
<span className="text-sm font-medium text-muted-foreground bg-muted/60 px-2 py-0.5 rounded-full">
140-
{entries.length}
141-
</span>
142-
)}
143-
</div>
144-
<p className="text-sm text-muted-foreground mt-1 flex items-center gap-1.5">
145-
<ShieldCheck className="h-3.5 w-3.5 text-emerald-600 dark:text-emerald-500" />
146-
AES-256-GCM encrypted on your device — server never sees plaintext.
147-
</p>
148-
</div>
141+
<div className="flex justify-end py-3 shrink-0">
149142
<AddApiKeyDialog />
150143
</div>
151144
)}
152145

153-
{isMobile && (
154-
<div className="px-4 pt-4 pb-2 shrink-0">
155-
<div className="flex items-center justify-between">
156-
<h1 className="text-2xl font-bold tracking-tight">API Keys</h1>
157-
{entries.length > 0 && (
158-
<span className="text-xs font-medium text-muted-foreground bg-muted/60 px-2 py-0.5 rounded-full">
159-
{entries.length} stored
160-
</span>
161-
)}
162-
</div>
163-
</div>
164-
)}
165-
166146
<div className="flex-1 min-h-0 flex flex-col">
167147
<ApiKeyList />
168148
</div>

apps/web/src/app/app/environment-manager/page.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { useEffect, useRef } from "react"
44
import { AddEnvironmentSetDialog } from "@/components/environment-manager/add-environment-set-dialog"
55
import { EnvironmentSetList } from "@/components/environment-manager/environment-set-list"
6+
import { ToolHeader } from "@/components/tools/tool-header"
67
import { useEnvironmentManagerStore, type EnvSetEntry } from "@/store/environment-manager-store"
78
import { useVaultGuard } from "@/hooks/use-vault-guard"
89
import { VaultLockedPlaceholder } from "@/components/vault-locked-placeholder"
@@ -118,9 +119,11 @@ export default function EnvironmentManagerPage() {
118119
}
119120
>
120121
{!isMobile && (
121-
<div className="flex justify-between items-center py-6 shrink-0">
122-
<h1 className="text-3xl font-bold tracking-tight">{t("title")}</h1>
123-
<AddEnvironmentSetDialog />
122+
<div className="shrink-0">
123+
<ToolHeader title={t("title")} toolId="/app/environment-manager" />
124+
<div className="flex justify-end py-3">
125+
<AddEnvironmentSetDialog />
126+
</div>
124127
</div>
125128
)}
126129

apps/web/src/app/dashboard/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ const DashboardPage: React.FC = () => {
273273
mobileOnly
274274
/>
275275

276-
<div className="px-3 md:px-8 pb-24 md:pb-12">
276+
<div className="px-3 pt-4 md:px-8 md:pt-8 pb-24 md:pb-12">
277277
<div className="max-w-7xl mx-auto space-y-5 md:space-y-8">
278278
{/* ── Desktop Hero (inside padded container for alignment) ── */}
279279
<RevealItem index={0}>

apps/web/src/components/base64/base64-layout.tsx

Lines changed: 42 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ import { cn } from '@/lib/utils';
1919
import { useTranslations } from 'next-intl';
2020
import { useSearchParams } from 'next/navigation';
2121
import { SendToMenu } from '@/components/ui/send-to-menu';
22+
import { IconTransform } from '@tabler/icons-react';
23+
import { ToolPageHeader } from '@/components/tools/tool-page-header';
24+
import { RevealItem } from '@/components/dashboard/dashboard-reveal';
25+
import { CATEGORY_ACCENT } from '@/components/dashboard/types';
2226

2327
type Mode = 'encode' | 'decode';
2428

@@ -152,39 +156,44 @@ export function Base64Layout() {
152156
const outputCharCount = output.length;
153157

154158
return (
155-
<div className="flex flex-col h-full gap-4">
156-
{/* Header */}
157-
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3 shrink-0 md:hidden">
158-
<div>
159-
<h1 className="text-lg font-semibold tracking-tight">{t('title')}</h1>
160-
<p className="text-xs text-muted-foreground">{t('subtitle')}</p>
161-
</div>
162-
<div className="flex items-center gap-2">
163-
<Button
164-
variant="outline"
165-
size="sm"
166-
className="gap-1.5 text-xs"
167-
onClick={() => fileInputRef.current?.click()}
168-
>
169-
<Upload className="h-3.5 w-3.5" />
170-
{t('upload')}
171-
</Button>
172-
<input
173-
ref={fileInputRef}
174-
type="file"
175-
accept=".txt,.b64,.base64,.json,.xml,.html,.css,.js,.ts,.md"
176-
className="hidden"
177-
onChange={(e) => {
178-
const file = e.target.files?.[0];
179-
if (file) handleFileUpload(file);
180-
e.target.value = '';
181-
}}
182-
/>
183-
<Button variant="outline" size="sm" className="gap-1.5 text-xs" onClick={handleClear}>
184-
<Trash2 className="h-3.5 w-3.5" />
185-
{t('clear')}
186-
</Button>
187-
</div>
159+
<div className="relative flex flex-col h-full gap-4 overflow-hidden dashboard-grid-bg">
160+
<div className="dash-ambient -z-10" aria-hidden />
161+
162+
<RevealItem index={0}>
163+
<ToolPageHeader
164+
icon={IconTransform}
165+
title={t('title')}
166+
description={t('subtitle')}
167+
accent={CATEGORY_ACCENT.Converters}
168+
/>
169+
</RevealItem>
170+
171+
{/* Mobile upload/clear actions */}
172+
<div className="flex items-center gap-2 shrink-0 md:hidden">
173+
<Button
174+
variant="outline"
175+
size="sm"
176+
className="gap-1.5 text-xs"
177+
onClick={() => fileInputRef.current?.click()}
178+
>
179+
<Upload className="h-3.5 w-3.5" />
180+
{t('upload')}
181+
</Button>
182+
<input
183+
ref={fileInputRef}
184+
type="file"
185+
accept=".txt,.b64,.base64,.json,.xml,.html,.css,.js,.ts,.md"
186+
className="hidden"
187+
onChange={(e) => {
188+
const file = e.target.files?.[0];
189+
if (file) handleFileUpload(file);
190+
e.target.value = '';
191+
}}
192+
/>
193+
<Button variant="outline" size="sm" className="gap-1.5 text-xs" onClick={handleClear}>
194+
<Trash2 className="h-3.5 w-3.5" />
195+
{t('clear')}
196+
</Button>
188197
</div>
189198

190199
{/* Mode Toggle */}

apps/web/src/components/csv-excel-json/csv-excel-json-tool.tsx

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ import { Card, CardContent } from "@/components/ui/card";
1717
import { Button } from "@/components/ui/button";
1818
import { Textarea } from "@/components/ui/textarea";
1919
import { Label } from "@/components/ui/label";
20-
import { ToolHeader } from "@/components/tools/tool-header";
20+
import { ToolPinButton } from "@/components/tools/tool-header";
21+
import { ToolPageHeader } from "@/components/tools/tool-page-header";
22+
import { RevealItem } from "@/components/dashboard/dashboard-reveal";
23+
import { CATEGORY_ACCENT } from "@/components/dashboard/types";
2124
import { useToolUsage } from "@/hooks/use-tool-usage";
2225
import { cn } from "@/lib/utils";
2326
import {
@@ -149,13 +152,23 @@ export function CsvExcelJsonTool() {
149152
};
150153

151154
return (
152-
<div className="flex flex-col h-full min-h-0 overflow-auto pb-4">
155+
<div className="relative flex flex-col h-full min-h-0 overflow-auto pb-4 dashboard-grid-bg">
156+
<div className="dash-ambient -z-10" aria-hidden />
157+
158+
<RevealItem index={0}>
159+
<div className="flex items-center justify-between gap-3 mb-4">
160+
<ToolPageHeader
161+
icon={IconFileSpreadsheet}
162+
title={t("title")}
163+
description={t("subtitle")}
164+
accent={CATEGORY_ACCENT.Converters}
165+
className="flex-1"
166+
/>
167+
<ToolPinButton toolId="csv-excel-json" />
168+
</div>
169+
</RevealItem>
170+
153171
<Card className="border-border/60 shadow-sm">
154-
<ToolHeader
155-
title={t("title")}
156-
description={t("subtitle")}
157-
toolId="csv-excel-json"
158-
/>
159172
<CardContent className="space-y-6 pt-2">
160173
<div
161174
role="button"

0 commit comments

Comments
 (0)