feat: replace broken @bytelyst/ui with sonner, adopt dashboard-components, use @bytelyst/extraction

Phase 0.5 of the execution roadmap:
- Remove phantom @bytelyst/ui dep, add sonner for toast notifications
- Replace custom error.tsx, not-found.tsx, loading.tsx with @bytelyst/dashboard-components
- Replace raw extraction-client.ts with @bytelyst/extraction createExtractionClient()
- Add @bytelyst/dashboard-components and @bytelyst/extraction to transpilePackages

Made-with: Cursor
This commit is contained in:
Saravana Achu Mac 2026-03-29 20:33:33 -07:00
parent f308a362fd
commit 7babee791d
8 changed files with 56 additions and 73 deletions

View File

@ -26,8 +26,10 @@ const nextConfig: NextConfig = {
transpilePackages: [
"@bytelyst/api-client",
"@bytelyst/blob-client",
"@bytelyst/dashboard-components",
"@bytelyst/design-tokens",
"@bytelyst/diagnostics-client",
"@bytelyst/extraction",
"@bytelyst/feature-flag-client",
"@bytelyst/kill-switch-client",
"@bytelyst/platform-client",

View File

@ -21,10 +21,12 @@
"@bytelyst/feature-flag-client": "^0.1.0",
"@bytelyst/kill-switch-client": "^0.1.0",
"@bytelyst/platform-client": "^0.1.0",
"@bytelyst/dashboard-components": "^0.1.0",
"@bytelyst/extraction": "^0.1.0",
"@bytelyst/react-auth": "^0.1.0",
"@bytelyst/telemetry-client": "^0.1.0",
"@bytelyst/ui": "file:../../learning_ai_common_plat/packages/ui",
"lucide-react": "^0.575.0",
"sonner": "^2.0.0",
"next": "16.1.6",
"react": "19.2.0",
"react-dom": "19.2.0",

View File

@ -1,25 +1,12 @@
'use client';
"use client";
import { useEffect } from 'react';
import { useEffect } from "react";
import { ErrorPage } from "@bytelyst/dashboard-components";
export default function Error({ error, reset }: { error: Error & { digest?: string }; reset: () => void }) {
useEffect(() => { console.error('Unhandled error:', error); }, [error]);
useEffect(() => {
console.error("Unhandled error:", error);
}, [error]);
return (
<div className="flex min-h-screen items-center justify-center p-4" style={{ background: 'var(--nl-bg-canvas, #06070A)' }}>
<div className="text-center">
<h1 className="text-4xl font-bold" style={{ color: 'var(--nl-danger, #FF6E6E)' }}>Something went wrong</h1>
<p className="mt-4 text-sm" style={{ color: 'var(--nl-text-secondary, #A5B1C7)' }}>
{error.message || 'An unexpected error occurred.'}
</p>
<button
onClick={reset}
className="mt-6 inline-flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium transition-colors"
style={{ background: 'var(--nl-accent-primary, #5A8CFF)', color: '#fff' }}
>
Try again
</button>
</div>
</div>
);
return <ErrorPage title="Something went wrong" message={error.message} onRetry={reset} />;
}

View File

@ -1,13 +1,9 @@
import { LoadingSpinner } from "@bytelyst/dashboard-components";
export default function Loading() {
return (
<div className="flex min-h-screen items-center justify-center" style={{ background: 'var(--nl-bg-canvas, #06070A)' }}>
<div className="flex flex-col items-center gap-3">
<div
className="h-8 w-8 animate-spin rounded-full border-2 border-t-transparent"
style={{ borderColor: 'var(--nl-accent-primary, #5A8CFF)', borderTopColor: 'transparent' }}
/>
<p className="text-sm" style={{ color: 'var(--nl-text-secondary, #A5B1C7)' }}>Loading...</p>
</div>
<div style={{ minHeight: "100vh", display: "grid", placeItems: "center", background: "var(--nl-bg-canvas, #06070A)" }}>
<LoadingSpinner size="lg" />
</div>
);
}

View File

@ -1,18 +1,15 @@
import Link from "next/link";
"use client";
export default function NotFoundPage() {
import { useRouter } from "next/navigation";
import { NotFoundPage } from "@bytelyst/dashboard-components";
export default function NotFound() {
const router = useRouter();
return (
<main style={{ minHeight: "100vh", display: "grid", placeItems: "center", padding: "var(--nl-space-8)" }}>
<section className="surface-card" style={{ padding: "var(--nl-space-8)", display: "grid", gap: "var(--nl-space-4)", maxWidth: 520 }}>
<div className="badge">Missing note or route</div>
<h1 style={{ margin: 0 }}>Not found</h1>
<p style={{ margin: 0, color: "var(--nl-text-secondary)" }}>
The requested note or view does not exist or is no longer available for your current workspace access.
</p>
<Link href="/dashboard" className="surface-muted" style={{ padding: "12px 16px", width: "fit-content" }}>
Back to dashboard
</Link>
</section>
</main>
<NotFoundPage
title="Not found"
message="The requested note or view does not exist or is no longer available for your current workspace access."
onBack={() => router.push("/dashboard")}
/>
);
}

View File

@ -2,10 +2,10 @@
import type { ReactNode } from "react";
import { useEffect } from "react";
import { Toaster } from "sonner";
import { AuthProvider } from "@/lib/auth";
import { initDiagnostics } from "@/lib/diagnostics";
import { initTelemetry } from "@/lib/telemetry";
import { ToastProvider } from "@bytelyst/ui";
export function Providers({ children }: { children: ReactNode }) {
useEffect(() => {
@ -14,8 +14,20 @@ export function Providers({ children }: { children: ReactNode }) {
}, []);
return (
<ToastProvider>
<>
<Toaster
position="top-right"
theme="dark"
toastOptions={{
style: {
background: "var(--nl-surface-card, #121725)",
border: "1px solid var(--nl-border-default)",
color: "var(--nl-text-primary, #EFF4FF)",
fontFamily: "var(--nl-font-body)",
},
}}
/>
<AuthProvider>{children}</AuthProvider>
</ToastProvider>
</>
);
}

View File

@ -1,36 +1,23 @@
"use client";
import { createApiClient } from "@bytelyst/api-client";
import { createExtractionClient, type ExtractionClient } from "@bytelyst/extraction";
import { EXTRACTION_SERVICE_URL, PRODUCT_ID } from "@/lib/product-config";
import type { NoteTask } from "@/lib/types";
type ExtractionEntity = {
extraction_class: string;
extraction_text: string;
attributes?: Record<string, string>;
};
type ExtractResponse = {
extractions: ExtractionEntity[];
};
function getAccessToken(): string | null {
if (typeof window === "undefined") {
return null;
}
if (typeof window === "undefined") return null;
return localStorage.getItem(`${PRODUCT_ID}_access_token`);
}
let _extractionApi: ReturnType<typeof createApiClient> | null = null;
function getExtractionApi() {
if (!_extractionApi) {
_extractionApi = createApiClient({
let _client: ExtractionClient | null = null;
function getClient(): ExtractionClient {
if (!_client) {
_client = createExtractionClient({
baseUrl: EXTRACTION_SERVICE_URL,
getToken: getAccessToken,
});
}
return _extractionApi;
return _client;
}
function slugify(value: string): string {
@ -42,13 +29,10 @@ function slugify(value: string): string {
}
export async function extractSuggestedTasks(text: string): Promise<NoteTask[]> {
const response = await getExtractionApi().fetch<ExtractResponse>("/api/extract", {
method: "POST",
body: JSON.stringify({
text,
taskId: "transcript-extraction",
productId: PRODUCT_ID,
}),
const response = await getClient().extract({
text,
taskId: "transcript-extraction",
productId: PRODUCT_ID,
});
return response.extractions

3
web/src/lib/toast.ts Normal file
View File

@ -0,0 +1,3 @@
"use client";
export { toast } from "sonner";