sarmaddev commited on
Commit
3318ac7
·
1 Parent(s): 5276811

added docker and nextjs app

Browse files
.env.example ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ # Manufacturing Agent - Frontend Configuration
2
+
3
+ # Backend API URL for analyze endpoint
4
+ # Local development: http://localhost:8080
5
+ # Production: Update with your backend deployment URL
6
+ NEXT_PUBLIC_API_URL=http://localhost:8080
.gitignore ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2
+
3
+ # dependencies
4
+ /node_modules
5
+ /.pnp
6
+ .pnp.*
7
+ .yarn/*
8
+ !.yarn/patches
9
+ !.yarn/plugins
10
+ !.yarn/releases
11
+ !.yarn/versions
12
+
13
+ # testing
14
+ /coverage
15
+
16
+ # next.js
17
+ /.next/
18
+ /out/
19
+
20
+ # production
21
+ /build
22
+
23
+ # misc
24
+ .DS_Store
25
+ *.pem
26
+
27
+ # debug
28
+ npm-debug.log*
29
+ yarn-debug.log*
30
+ yarn-error.log*
31
+ .pnpm-debug.log*
32
+
33
+ # env files (can opt-in for committing if needed)
34
+ .env
35
+ .env.local
36
+
37
+ # vercel
38
+ .vercel
39
+
40
+ # typescript
41
+ *.tsbuildinfo
42
+ next-env.d.ts
AGENTS.md ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ <!-- BEGIN:nextjs-agent-rules -->
2
+ # This is NOT the Next.js you know
3
+
4
+ This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices.
5
+ <!-- END:nextjs-agent-rules -->
CLAUDE.md ADDED
@@ -0,0 +1 @@
 
 
1
+ @AGENTS.md
README.md CHANGED
@@ -1,11 +1,36 @@
1
- ---
2
- title: MachinaCheck
3
- emoji: 🐢
4
- colorFrom: gray
5
- colorTo: purple
6
- sdk: docker
7
- pinned: false
8
- short_description: Ma nufacturing Agents
9
- ---
10
-
11
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
2
+
3
+ ## Getting Started
4
+
5
+ First, run the development server:
6
+
7
+ ```bash
8
+ npm run dev
9
+ # or
10
+ yarn dev
11
+ # or
12
+ pnpm dev
13
+ # or
14
+ bun dev
15
+ ```
16
+
17
+ Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18
+
19
+ You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
20
+
21
+ This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
22
+
23
+ ## Learn More
24
+
25
+ To learn more about Next.js, take a look at the following resources:
26
+
27
+ - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28
+ - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
29
+
30
+ You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
31
+
32
+ ## Deploy on Vercel
33
+
34
+ The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
35
+
36
+ Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
app/analyze/page.tsx ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import { useAnalyze } from "@/hooks/use-analyze";
5
+ import { UploadZone } from "@/components/analyze/upload-zone";
6
+ import { AnalyzeForm } from "@/components/analyze/analyze-form";
7
+ import { LoadingState } from "@/components/analyze/loading-state";
8
+ import { AnalysisResult } from "@/components/analyze/analysis-result";
9
+ import { Button } from "@/components/ui/button";
10
+ import { Navbar } from "@/components/landing/navbar";
11
+ import { Footer } from "@/components/landing/footer";
12
+ import { AlertCircle, ArrowRight } from "lucide-react";
13
+
14
+ export default function AnalyzePage() {
15
+ const {
16
+ form,
17
+ selectedFile,
18
+ handleFileSelect,
19
+ handleFileRemove,
20
+ onSubmit,
21
+ result,
22
+ error,
23
+ isLoading,
24
+ resetAnalysis,
25
+ } = useAnalyze();
26
+
27
+ const [isDragging, setIsDragging] = useState(false);
28
+
29
+ if (result) {
30
+ return (
31
+ <>
32
+ <Navbar />
33
+ <div className="min-h-screen bg-gradient-to-br from-slate-950 via-slate-900 to-slate-950 pt-20 pb-16 px-4">
34
+ <div className="absolute inset-0 overflow-hidden pointer-events-none">
35
+ <div className="absolute top-20 left-1/4 w-96 h-96 bg-blue-500/10 rounded-full blur-3xl"></div>
36
+ <div className="absolute bottom-20 right-1/4 w-80 h-80 bg-cyan-500/10 rounded-full blur-3xl"></div>
37
+ </div>
38
+ <div className="max-w-5xl mx-auto relative z-10">
39
+ <AnalysisResult data={result} />
40
+ <div className="mt-10 flex gap-4 justify-center">
41
+ <Button
42
+ onClick={resetAnalysis}
43
+ className="px-8 py-3 bg-gradient-to-r from-blue-600 to-cyan-500 hover:from-blue-700 hover:to-cyan-600 text-white rounded-lg font-semibold transition-all active:scale-95 shadow-lg"
44
+ >
45
+ <ArrowRight className="w-4 h-4 mr-2" />
46
+ Analyze Another File
47
+ </Button>
48
+ </div>
49
+ </div>
50
+ </div>
51
+ <Footer />
52
+ </>
53
+ );
54
+ }
55
+
56
+ return (
57
+ <>
58
+ <Navbar />
59
+ <div className="min-h-screen bg-gradient-to-br from-slate-950 via-slate-900 to-slate-950 pt-20 pb-16 px-4 sm:px-6 lg:px-8">
60
+ <div className="absolute inset-0 overflow-hidden pointer-events-none">
61
+ <div className="absolute top-20 left-1/4 w-96 h-96 bg-blue-500/10 rounded-full blur-3xl"></div>
62
+ <div className="absolute bottom-20 right-1/4 w-80 h-80 bg-cyan-500/10 rounded-full blur-3xl"></div>
63
+ </div>
64
+
65
+ <div className="max-w-5xl mx-auto relative z-10">
66
+ <div className="mb-16 text-center">
67
+ <div className="inline-block mb-4 px-3 py-1 bg-blue-500/10 border border-blue-500/30 rounded-full">
68
+ <span className="text-xs font-semibold text-blue-400 uppercase tracking-widest">Precision Analysis</span>
69
+ </div>
70
+ <h1 className="text-5xl sm:text-6xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-blue-400 to-cyan-300 mb-4 tracking-tight">
71
+ Manufacturability Check
72
+ </h1>
73
+ <p className="text-lg text-slate-400 max-w-2xl mx-auto leading-relaxed">
74
+ Upload your STEP file and specify parameters to instantly verify manufacturing feasibility
75
+ </p>
76
+ </div>
77
+
78
+ <div className="grid lg:grid-cols-3 gap-8 items-start">
79
+ <div className="lg:col-span-2 space-y-6">
80
+ <div className="bg-gradient-to-br from-slate-800/50 to-slate-900/50 border border-slate-700/50 rounded-2xl p-8 backdrop-blur-sm hover:border-blue-500/30 transition-all duration-300">
81
+ <label className="text-xs font-semibold uppercase tracking-widest text-slate-300 mb-4 block">📁 Upload STEP File</label>
82
+ <UploadZone
83
+ file={selectedFile}
84
+ onFileSelect={handleFileSelect}
85
+ onFileRemove={handleFileRemove}
86
+ isDragging={isDragging}
87
+ onDragEnter={() => setIsDragging(true)}
88
+ onDragLeave={() => setIsDragging(false)}
89
+ isDisabled={isLoading}
90
+ />
91
+ </div>
92
+
93
+ {isLoading && <LoadingState />}
94
+
95
+ {error && (
96
+ <div className="border border-red-500/30 rounded-xl p-5 bg-red-950/20 backdrop-blur-sm flex gap-4">
97
+ <AlertCircle className="w-5 h-5 text-red-400 flex-shrink-0 mt-1" />
98
+ <div className="flex-1">
99
+ <p className="text-sm font-semibold text-red-200">Analysis Failed</p>
100
+ <p className="text-sm text-red-300 mt-1">{error}</p>
101
+ </div>
102
+ </div>
103
+ )}
104
+ </div>
105
+
106
+ <div className="lg:col-span-1">
107
+ <div className="sticky top-24 bg-gradient-to-br from-slate-800/50 to-slate-900/50 border border-slate-700/50 rounded-2xl p-6 backdrop-blur-sm">
108
+ <label className="text-xs font-semibold uppercase tracking-widest text-slate-300 mb-4 block">⚙️ Parameters</label>
109
+ <AnalyzeForm
110
+ form={form}
111
+ onSubmit={onSubmit}
112
+ isLoading={isLoading}
113
+ isDisabled={!selectedFile}
114
+ />
115
+ </div>
116
+ </div>
117
+ </div>
118
+ </div>
119
+ </div>
120
+ <Footer />
121
+ </>
122
+ );
123
+ }
app/favicon.ico ADDED
app/globals.css ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @import "tailwindcss";
2
+ @import "tw-animate-css";
3
+ @import "shadcn/tailwind.css";
4
+
5
+ @custom-variant dark (&:is(.dark *));
6
+
7
+ @theme inline {
8
+ --color-background: var(--background);
9
+ --color-foreground: var(--foreground);
10
+ --font-sans: var(--font-sans);
11
+ --font-mono: var(--font-geist-mono);
12
+ --font-heading: var(--font-sans);
13
+ --color-sidebar-ring: var(--sidebar-ring);
14
+ --color-sidebar-border: var(--sidebar-border);
15
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
16
+ --color-sidebar-accent: var(--sidebar-accent);
17
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
18
+ --color-sidebar-primary: var(--sidebar-primary);
19
+ --color-sidebar-foreground: var(--sidebar-foreground);
20
+ --color-sidebar: var(--sidebar);
21
+ --color-chart-5: var(--chart-5);
22
+ --color-chart-4: var(--chart-4);
23
+ --color-chart-3: var(--chart-3);
24
+ --color-chart-2: var(--chart-2);
25
+ --color-chart-1: var(--chart-1);
26
+ --color-ring: var(--ring);
27
+ --color-input: var(--input);
28
+ --color-border: var(--border);
29
+ --color-destructive: var(--destructive);
30
+ --color-accent-foreground: var(--accent-foreground);
31
+ --color-accent: var(--accent);
32
+ --color-muted-foreground: var(--muted-foreground);
33
+ --color-muted: var(--muted);
34
+ --color-secondary-foreground: var(--secondary-foreground);
35
+ --color-secondary: var(--secondary);
36
+ --color-primary-foreground: var(--primary-foreground);
37
+ --color-primary: var(--primary);
38
+ --color-popover-foreground: var(--popover-foreground);
39
+ --color-popover: var(--popover);
40
+ --color-card-foreground: var(--card-foreground);
41
+ --color-card: var(--card);
42
+ --radius-sm: calc(var(--radius) * 0.6);
43
+ --radius-md: calc(var(--radius) * 0.8);
44
+ --radius-lg: var(--radius);
45
+ --radius-xl: calc(var(--radius) * 1.4);
46
+ --radius-2xl: calc(var(--radius) * 1.8);
47
+ --radius-3xl: calc(var(--radius) * 2.2);
48
+ --radius-4xl: calc(var(--radius) * 2.6);
49
+ }
50
+
51
+ :root {
52
+ --background: oklch(1 0 0);
53
+ --foreground: oklch(0.145 0 0);
54
+ --card: oklch(1 0 0);
55
+ --card-foreground: oklch(0.145 0 0);
56
+ --popover: oklch(1 0 0);
57
+ --popover-foreground: oklch(0.145 0 0);
58
+ --primary: oklch(0.205 0 0);
59
+ --primary-foreground: oklch(0.985 0 0);
60
+ --secondary: oklch(0.97 0 0);
61
+ --secondary-foreground: oklch(0.205 0 0);
62
+ --muted: oklch(0.97 0 0);
63
+ --muted-foreground: oklch(0.556 0 0);
64
+ --accent: oklch(0.97 0 0);
65
+ --accent-foreground: oklch(0.205 0 0);
66
+ --destructive: oklch(0.577 0.245 27.325);
67
+ --border: oklch(0.922 0 0);
68
+ --input: oklch(0.922 0 0);
69
+ --ring: oklch(0.708 0 0);
70
+ --chart-1: oklch(0.87 0 0);
71
+ --chart-2: oklch(0.556 0 0);
72
+ --chart-3: oklch(0.439 0 0);
73
+ --chart-4: oklch(0.371 0 0);
74
+ --chart-5: oklch(0.269 0 0);
75
+ --radius: 0.625rem;
76
+ --sidebar: oklch(0.985 0 0);
77
+ --sidebar-foreground: oklch(0.145 0 0);
78
+ --sidebar-primary: oklch(0.205 0 0);
79
+ --sidebar-primary-foreground: oklch(0.985 0 0);
80
+ --sidebar-accent: oklch(0.97 0 0);
81
+ --sidebar-accent-foreground: oklch(0.205 0 0);
82
+ --sidebar-border: oklch(0.922 0 0);
83
+ --sidebar-ring: oklch(0.708 0 0);
84
+ }
85
+
86
+ .dark {
87
+ --background: oklch(0.145 0 0);
88
+ --foreground: oklch(0.985 0 0);
89
+ --card: oklch(0.205 0 0);
90
+ --card-foreground: oklch(0.985 0 0);
91
+ --popover: oklch(0.205 0 0);
92
+ --popover-foreground: oklch(0.985 0 0);
93
+ --primary: oklch(0.922 0 0);
94
+ --primary-foreground: oklch(0.205 0 0);
95
+ --secondary: oklch(0.269 0 0);
96
+ --secondary-foreground: oklch(0.985 0 0);
97
+ --muted: oklch(0.269 0 0);
98
+ --muted-foreground: oklch(0.708 0 0);
99
+ --accent: oklch(0.269 0 0);
100
+ --accent-foreground: oklch(0.985 0 0);
101
+ --destructive: oklch(0.704 0.191 22.216);
102
+ --border: oklch(1 0 0 / 10%);
103
+ --input: oklch(1 0 0 / 15%);
104
+ --ring: oklch(0.556 0 0);
105
+ --chart-1: oklch(0.87 0 0);
106
+ --chart-2: oklch(0.556 0 0);
107
+ --chart-3: oklch(0.439 0 0);
108
+ --chart-4: oklch(0.371 0 0);
109
+ --chart-5: oklch(0.269 0 0);
110
+ --sidebar: oklch(0.205 0 0);
111
+ --sidebar-foreground: oklch(0.985 0 0);
112
+ --sidebar-primary: oklch(0.488 0.243 264.376);
113
+ --sidebar-primary-foreground: oklch(0.985 0 0);
114
+ --sidebar-accent: oklch(0.269 0 0);
115
+ --sidebar-accent-foreground: oklch(0.985 0 0);
116
+ --sidebar-border: oklch(1 0 0 / 10%);
117
+ --sidebar-ring: oklch(0.556 0 0);
118
+ }
119
+
120
+ @layer base {
121
+ html {
122
+ @apply font-sans scroll-smooth;
123
+ }
124
+
125
+ * {
126
+ @apply border-border outline-ring/50;
127
+ }
128
+
129
+ body {
130
+ @apply bg-background text-foreground;
131
+ }
132
+ }
133
+
134
+ /* Smooth transitions for interactive elements */
135
+ @layer components {
136
+ button,
137
+ a,
138
+ input,
139
+ select,
140
+ textarea {
141
+ @apply transition-all duration-300 ease-in-out;
142
+ }
143
+
144
+ /* Specific smooth transitions for buttons */
145
+ button {
146
+ @apply active:scale-95 focus:outline-none focus:ring-2 focus:ring-offset-2;
147
+ }
148
+
149
+ /* Link hover effects */
150
+ a {
151
+ @apply hover:text-cyan-300;
152
+ }
153
+
154
+ /* Form element transitions */
155
+ input,
156
+ textarea,
157
+ select {
158
+ @apply focus:ring-2 focus:ring-cyan-500/50 focus:border-cyan-400;
159
+ }
160
+ }
app/layout.tsx ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import type { Metadata } from "next";
2
+ import { Geist, Geist_Mono } from "next/font/google";
3
+ import "./globals.css";
4
+
5
+ const geistSans = Geist({
6
+ variable: "--font-geist-sans",
7
+ subsets: ["latin"],
8
+ });
9
+
10
+ const geistMono = Geist_Mono({
11
+ variable: "--font-geist-mono",
12
+ subsets: ["latin"],
13
+ });
14
+
15
+ export const metadata: Metadata = {
16
+ title: "Create Next App",
17
+ description: "Generated by create next app",
18
+ };
19
+
20
+ export default function RootLayout({
21
+ children,
22
+ }: Readonly<{
23
+ children: React.ReactNode;
24
+ }>) {
25
+ return (
26
+ <html
27
+ lang="en"
28
+ className={`${geistSans.variable} ${geistMono.variable} h-full antialiased dark`}
29
+ >
30
+ <body className="min-h-full flex flex-col">{children}</body>
31
+ </html>
32
+ );
33
+ }
app/page.tsx ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Navbar } from "@/components/landing/navbar";
2
+ import { Hero } from "@/components/landing/hero";
3
+ import { Features } from "@/components/landing/features";
4
+ import { Workflow } from "@/components/landing/workflow";
5
+ import { DashboardPreview } from "@/components/landing/dashboard-preview";
6
+ import { CTA } from "@/components/landing/cta";
7
+ import { Footer } from "@/components/landing/footer";
8
+
9
+ export default function Home() {
10
+ return (
11
+ <div className="flex flex-col min-h-screen">
12
+ <Navbar />
13
+ <main className="flex-1">
14
+ <Hero />
15
+ <Features />
16
+ <Workflow />
17
+ <DashboardPreview />
18
+ <CTA />
19
+ </main>
20
+ <Footer />
21
+ </div>
22
+ );
23
+ }
components.json ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "radix-nova",
4
+ "rsc": true,
5
+ "tsx": true,
6
+ "tailwind": {
7
+ "config": "",
8
+ "css": "app/globals.css",
9
+ "baseColor": "neutral",
10
+ "cssVariables": true,
11
+ "prefix": ""
12
+ },
13
+ "iconLibrary": "lucide",
14
+ "rtl": false,
15
+ "aliases": {
16
+ "components": "@/components",
17
+ "utils": "@/lib/utils",
18
+ "ui": "@/components/ui",
19
+ "lib": "@/lib",
20
+ "hooks": "@/hooks"
21
+ },
22
+ "menuColor": "default",
23
+ "menuAccent": "subtle",
24
+ "registries": {}
25
+ }
components/analyze/analysis-result.tsx ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client";
2
+
3
+ import { AnalysisResponse } from "@/lib/types";
4
+ import { StatusBadge } from "./status-badge";
5
+ import { Wrench, Lightbulb, AlertCircle, Zap } from "lucide-react";
6
+
7
+ interface AnalysisResultProps {
8
+ data: AnalysisResponse;
9
+ }
10
+
11
+ export function AnalysisResult({ data }: AnalysisResultProps) {
12
+ return (
13
+ <div className="space-y-8 animate-in fade-in duration-500">
14
+ <div className="border-b border-gray-700 pb-6 space-y-3">
15
+ <div className="flex items-start justify-between gap-4">
16
+ <div className="space-y-1 flex-1">
17
+ <h2 className="text-2xl font-mono font-bold text-gray-100 uppercase tracking-wide">
18
+ ANALYSIS COMPLETE
19
+ </h2>
20
+ <p className="text-xs text-gray-500 font-mono">
21
+ {new Date().toLocaleString()}
22
+ </p>
23
+ </div>
24
+ <StatusBadge decision={data.decision} />
25
+ </div>
26
+ </div>
27
+
28
+ <div className="bg-gray-900/50 border border-gray-700/50 rounded p-4 space-y-2">
29
+ <p className="text-sm text-gray-200 leading-relaxed font-mono">
30
+ {data.summary}
31
+ </p>
32
+ </div>
33
+
34
+ <div className="grid md:grid-cols-2 gap-6">
35
+ {data.operations && data.operations.length > 0 && (
36
+ <div className="border border-gray-700/50 rounded p-4 space-y-3">
37
+ <h3 className="text-xs font-mono uppercase tracking-wider text-blue-400 font-semibold flex items-center gap-2">
38
+ <Wrench className="w-4 h-4" />
39
+ REQUIRED OPERATIONS
40
+ </h3>
41
+ <ul className="space-y-2">
42
+ {data.operations.map((op, idx) => (
43
+ <li
44
+ key={idx}
45
+ className="text-sm text-gray-300 font-mono flex items-start gap-2"
46
+ >
47
+ <span className="mt-1.5 w-1.5 h-1.5 rounded-full bg-blue-500 flex-shrink-0" />
48
+ <span>{op}</span>
49
+ </li>
50
+ ))}
51
+ </ul>
52
+ </div>
53
+ )}
54
+
55
+ {data.required_tools && data.required_tools.length > 0 && (
56
+ <div className="border border-gray-700/50 rounded p-4 space-y-3">
57
+ <h3 className="text-xs font-mono uppercase tracking-wider text-green-400 font-semibold flex items-center gap-2">
58
+ <Zap className="w-4 h-4" />
59
+ AVAILABLE TOOLS
60
+ </h3>
61
+ <ul className="space-y-2">
62
+ {data.required_tools.map((tool, idx) => (
63
+ <li
64
+ key={idx}
65
+ className="text-sm text-gray-300 font-mono flex items-start gap-2"
66
+ >
67
+ <span className="mt-1.5 w-1.5 h-1.5 rounded-full bg-green-500 flex-shrink-0" />
68
+ <span>{tool}</span>
69
+ </li>
70
+ ))}
71
+ </ul>
72
+ </div>
73
+ )}
74
+ </div>
75
+
76
+ {data.missing_tools && data.missing_tools.length > 0 && (
77
+ <div className="border border-red-700/50 bg-red-950/20 rounded p-4 space-y-3">
78
+ <h3 className="text-xs font-mono uppercase tracking-wider text-red-400 font-semibold flex items-center gap-2">
79
+ <AlertCircle className="w-4 h-4" />
80
+ MISSING TOOLS
81
+ </h3>
82
+ <ul className="space-y-2">
83
+ {data.missing_tools.map((tool, idx) => (
84
+ <li
85
+ key={idx}
86
+ className="text-sm text-red-300 font-mono flex items-start gap-2"
87
+ >
88
+ <span className="mt-1.5 w-1.5 h-1.5 rounded-full bg-red-500 flex-shrink-0" />
89
+ <span>{tool}</span>
90
+ </li>
91
+ ))}
92
+ </ul>
93
+ </div>
94
+ )}
95
+
96
+ {data.recommendations && data.recommendations.length > 0 && (
97
+ <div className="border border-blue-700/50 bg-blue-950/20 rounded p-4 space-y-3">
98
+ <h3 className="text-xs font-mono uppercase tracking-wider text-blue-400 font-semibold flex items-center gap-2">
99
+ <Lightbulb className="w-4 h-4" />
100
+ RECOMMENDATIONS
101
+ </h3>
102
+ <ul className="space-y-2">
103
+ {data.recommendations.map((rec, idx) => (
104
+ <li
105
+ key={idx}
106
+ className="text-sm text-blue-300 font-mono flex items-start gap-2"
107
+ >
108
+ <span className="mt-1.5 w-1.5 h-1.5 rounded-full bg-blue-500 flex-shrink-0" />
109
+ <span>{rec}</span>
110
+ </li>
111
+ ))}
112
+ </ul>
113
+ </div>
114
+ )}
115
+ </div>
116
+ );
117
+ }
components/analyze/analyze-form.tsx ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client";
2
+
3
+ import { Input } from "@/components/ui/input";
4
+ import { Label } from "@/components/ui/label";
5
+ import { Button } from "@/components/ui/button";
6
+ import { UseFormReturn, FieldValues } from "react-hook-form";
7
+ import { Loader2 } from "lucide-react";
8
+
9
+ interface AnalyzeFormProps {
10
+ form: UseFormReturn<any>;
11
+ onSubmit: () => Promise<void>;
12
+ isLoading: boolean;
13
+ isDisabled: boolean;
14
+ }
15
+
16
+ export function AnalyzeForm({
17
+ form,
18
+ onSubmit,
19
+ isLoading,
20
+ isDisabled,
21
+ }: AnalyzeFormProps) {
22
+ const { register, formState } = form;
23
+
24
+ return (
25
+ <form onSubmit={onSubmit} className="space-y-4">
26
+ <div className="space-y-2">
27
+ <Label className="text-xs font-semibold uppercase tracking-wider text-slate-300">Material</Label>
28
+ <Input
29
+ {...register("material")}
30
+ placeholder="Steel 304, Aluminium 6061"
31
+ disabled={isLoading || isDisabled}
32
+ className="text-sm bg-slate-700/50 border-slate-600 text-slate-100 placeholder:text-slate-500 focus:border-blue-400 focus:ring-blue-500/30 transition-all duration-300 hover:border-slate-500"
33
+ />
34
+ {formState.errors.material && (
35
+ <p className="text-xs text-red-400">{formState.errors.material.message as string}</p>
36
+ )}
37
+ </div>
38
+
39
+ <div className="space-y-2">
40
+ <Label className="text-xs font-semibold uppercase tracking-wider text-slate-300">Tolerance</Label>
41
+ <Input
42
+ {...register("tolerance")}
43
+ placeholder="±0.02mm, ±0.05mm"
44
+ disabled={isLoading || isDisabled}
45
+ className="text-sm bg-slate-700/50 border-slate-600 text-slate-100 placeholder:text-slate-500 focus:border-blue-400 focus:ring-blue-500/30 transition-all duration-300 hover:border-slate-500"
46
+ />
47
+ {formState.errors.tolerance && (
48
+ <p className="text-xs text-red-400">{formState.errors.tolerance.message as string}</p>
49
+ )}
50
+ </div>
51
+
52
+ <div className="space-y-2">
53
+ <Label className="text-xs font-semibold uppercase tracking-wider text-slate-300">Threads (Optional)</Label>
54
+ <Input
55
+ {...register("threads")}
56
+ placeholder="M8x1.25, M10x1.5"
57
+ disabled={isLoading || isDisabled}
58
+ className="text-sm bg-slate-700/50 border-slate-600 text-slate-100 placeholder:text-slate-500 focus:border-blue-400 focus:ring-blue-500/30 transition-all duration-300 hover:border-slate-500"
59
+ />
60
+ </div>
61
+
62
+ <Button
63
+ type="submit"
64
+ disabled={isLoading || isDisabled}
65
+ className="w-full h-11 font-semibold text-sm bg-gradient-to-r from-blue-600 to-cyan-500 hover:from-blue-700 hover:to-cyan-600 text-white rounded-lg transition-all duration-300 disabled:opacity-50 disabled:cursor-not-allowed active:scale-95 hover:scale-105 hover:shadow-lg hover:shadow-blue-500/50 shadow-lg"
66
+ >
67
+ {isLoading ? (
68
+ <>
69
+ <Loader2 className="w-4 h-4 mr-2 animate-spin" />
70
+ Analyzing...
71
+ </>
72
+ ) : (
73
+ "Analyze Now"
74
+ )}
75
+ </Button>
76
+ </form>
77
+ );
78
+ }
components/analyze/loading-state.tsx ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client";
2
+
3
+ import { Loader2 } from "lucide-react";
4
+
5
+ export function LoadingState() {
6
+ return (
7
+ <div className="flex flex-col items-center justify-center py-16 gap-6 border-2 border-dashed border-gray-600 rounded-lg bg-gray-950/50 p-12">
8
+ <div className="p-4 rounded-lg bg-blue-950/30 border border-blue-700/30">
9
+ <Loader2 className="w-8 h-8 text-blue-400 animate-spin" />
10
+ </div>
11
+ <div className="text-center space-y-2">
12
+ <p className="text-sm font-mono font-semibold text-gray-200 uppercase tracking-wide">
13
+ ANALYZING MANUFACTURABILITY...
14
+ </p>
15
+ <p className="text-xs text-gray-500 font-mono">
16
+ Processing CAD geometry and checking tool compatibility
17
+ </p>
18
+ </div>
19
+ </div>
20
+ );
21
+ }
components/analyze/status-badge.tsx ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client";
2
+
3
+ import { DecisionType } from "@/lib/types";
4
+ import { Check, X, AlertCircle } from "lucide-react";
5
+
6
+ interface StatusBadgeProps {
7
+ decision: DecisionType;
8
+ }
9
+
10
+ export function StatusBadge({ decision }: StatusBadgeProps) {
11
+ const config = {
12
+ YES: {
13
+ bg: "bg-green-950/40 border-green-700/50",
14
+ text: "text-green-400",
15
+ icon: <Check className="w-5 h-5" />,
16
+ label: "MANUFACTURABLE",
17
+ },
18
+ NO: {
19
+ bg: "bg-red-950/40 border-red-700/50",
20
+ text: "text-red-400",
21
+ icon: <X className="w-5 h-5" />,
22
+ label: "NOT MANUFACTURABLE",
23
+ },
24
+ CONDITIONAL: {
25
+ bg: "bg-yellow-950/40 border-yellow-700/50",
26
+ text: "text-yellow-400",
27
+ icon: <AlertCircle className="w-5 h-5" />,
28
+ label: "CONDITIONALLY MANUFACTURABLE",
29
+ },
30
+ }[decision];
31
+
32
+ return (
33
+ <div
34
+ className={`inline-flex items-center gap-3 px-4 py-3 rounded border font-mono text-sm font-semibold uppercase tracking-wide ${config.bg}`}
35
+ >
36
+ <span className={config.text}>{config.icon}</span>
37
+ <span className={config.text}>{config.label}</span>
38
+ </div>
39
+ );
40
+ }
components/analyze/upload-zone.tsx ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client";
2
+
3
+ import { useRef, useState } from "react";
4
+ import { Upload, X } from "lucide-react";
5
+ import { cn } from "@/lib/utils";
6
+
7
+ interface UploadZoneProps {
8
+ file: File | null;
9
+ onFileSelect: (file: File) => void;
10
+ onFileRemove: () => void;
11
+ isDragging: boolean;
12
+ onDragEnter: () => void;
13
+ onDragLeave: () => void;
14
+ isDisabled?: boolean;
15
+ }
16
+
17
+ export function UploadZone({
18
+ file,
19
+ onFileSelect,
20
+ onFileRemove,
21
+ isDragging,
22
+ onDragEnter,
23
+ onDragLeave,
24
+ isDisabled,
25
+ }: UploadZoneProps) {
26
+ const inputRef = useRef<HTMLInputElement>(null);
27
+
28
+ const handleDragOver = (e: React.DragEvent) => {
29
+ e.preventDefault();
30
+ };
31
+
32
+ const handleDrop = (e: React.DragEvent) => {
33
+ e.preventDefault();
34
+ onDragLeave();
35
+
36
+ const droppedFile = e.dataTransfer.files?.[0];
37
+ if (droppedFile) {
38
+ onFileSelect(droppedFile);
39
+ }
40
+ };
41
+
42
+ const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
43
+ const selectedFile = e.currentTarget.files?.[0];
44
+ if (selectedFile) {
45
+ onFileSelect(selectedFile);
46
+ }
47
+ };
48
+
49
+ const handleClick = () => {
50
+ if (!isDisabled) {
51
+ inputRef.current?.click();
52
+ }
53
+ };
54
+
55
+ if (file) {
56
+ return (
57
+ <div className="border border-blue-500/30 rounded-xl p-5 bg-gradient-to-r from-blue-950/30 to-cyan-950/30 backdrop-blur-sm transition-all duration-300">
58
+ <div className="flex items-center justify-between gap-4">
59
+ <div className="flex items-center gap-3 flex-1">
60
+ <div className="p-3 rounded-lg bg-gradient-to-br from-blue-500/20 to-cyan-500/20 border border-blue-500/40 transition-all duration-300">
61
+ <Upload className="w-5 h-5 text-blue-300" />
62
+ </div>
63
+ <div className="min-w-0 flex-1">
64
+ <p className="text-sm font-semibold text-blue-100 truncate">{file.name}</p>
65
+ <p className="text-xs text-blue-400/70">{(file.size / 1024).toFixed(2)} KB</p>
66
+ </div>
67
+ </div>
68
+ <button
69
+ onClick={onFileRemove}
70
+ disabled={isDisabled}
71
+ className="p-2 hover:bg-red-500/20 rounded-lg transition-all duration-300 disabled:opacity-50 hover:scale-110 active:scale-95"
72
+ type="button"
73
+ >
74
+ <X className="w-4 h-4 text-red-400" />
75
+ </button>
76
+ </div>
77
+ </div>
78
+ );
79
+ }
80
+
81
+ return (
82
+ <div
83
+ onDragOver={handleDragOver}
84
+ onDrop={handleDrop}
85
+ onDragEnter={onDragEnter}
86
+ onDragLeave={onDragLeave}
87
+ onClick={handleClick}
88
+ className={cn(
89
+ "relative border-2 border-dashed rounded-2xl p-12 transition-all duration-300 cursor-pointer",
90
+ isDragging
91
+ ? "border-blue-400 bg-blue-950/40 scale-105"
92
+ : "border-slate-600 hover:border-slate-500 bg-slate-950/40 hover:bg-slate-950/60 hover:scale-102",
93
+ isDisabled && "opacity-50 cursor-not-allowed"
94
+ )}
95
+ >
96
+ <input
97
+ ref={inputRef}
98
+ type="file"
99
+ accept=".step,.stp"
100
+ onChange={handleInputChange}
101
+ disabled={isDisabled}
102
+ className="hidden"
103
+ />
104
+
105
+ <div className="flex flex-col items-center justify-center gap-4 text-center">
106
+ <div className="p-4 rounded-xl bg-gradient-to-br from-blue-500/20 to-cyan-500/20 border border-blue-500/30">
107
+ <Upload className="w-8 h-8 text-blue-400" />
108
+ </div>
109
+ <div>
110
+ <p className="text-base font-semibold text-slate-100">Drag & Drop CAD Files Here</p>
111
+ <p className="text-sm text-slate-400 mt-1">Supported: STEP, STP (Max 50MB)</p>
112
+ </div>
113
+ <button
114
+ type="button"
115
+ disabled={isDisabled}
116
+ className={cn(
117
+ "px-6 py-2.5 text-sm font-semibold rounded-lg transition-all duration-300",
118
+ isDisabled
119
+ ? "bg-slate-700 text-slate-400 cursor-not-allowed"
120
+ : "bg-gradient-to-r from-blue-600 to-cyan-500 hover:from-blue-700 hover:to-cyan-600 text-white shadow-lg hover:shadow-blue-500/50 active:scale-95 hover:scale-105"
121
+ )}
122
+ >
123
+ Select Files
124
+ </button>
125
+ </div>
126
+ </div>
127
+ );
128
+ }
components/landing/cta.tsx ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from "react";
2
+ import Link from "next/link";
3
+ import { Upload, ArrowRight } from "lucide-react";
4
+
5
+ export function CTA() {
6
+ return (
7
+ <section className="py-32 relative overflow-hidden bg-gradient-to-b from-slate-950 via-slate-900 to-slate-950 border-t border-slate-800">
8
+ <div className="absolute inset-0 overflow-hidden pointer-events-none">
9
+ <div className="absolute top-1/4 left-0 w-96 h-96 bg-blue-600/15 rounded-full blur-3xl"></div>
10
+ <div className="absolute bottom-0 right-1/3 w-80 h-80 bg-cyan-600/15 rounded-full blur-3xl"></div>
11
+ </div>
12
+
13
+ <div className="container mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
14
+ <div className="max-w-4xl mx-auto">
15
+ <div className="bg-gradient-to-br from-slate-800/50 to-slate-900/50 border border-slate-700/50 rounded-2xl p-12 sm:p-16 text-center backdrop-blur-sm">
16
+ <h2 className="text-4xl sm:text-5xl lg:text-6xl font-bold tracking-tight mb-6 text-transparent bg-clip-text bg-gradient-to-r from-blue-300 to-cyan-300">
17
+ Ready to optimize your CNC workflow?
18
+ </h2>
19
+ <p className="text-xl text-slate-300 leading-relaxed mb-10 max-w-2xl mx-auto">
20
+ Join leading machine shops eliminating quoting delays, reducing errors, and maximizing shop floor efficiency with AI-powered manufacturability analysis.
21
+ </p>
22
+
23
+ <div className="flex flex-col sm:flex-row gap-4 justify-center mb-8">
24
+ <Link
25
+ href="/analyze"
26
+ className="inline-flex items-center justify-center gap-2 px-8 py-4 rounded-lg bg-gradient-to-r from-blue-600 to-cyan-500 hover:from-blue-700 hover:to-cyan-600 text-white font-semibold shadow-lg hover:shadow-blue-500/50 transition-all active:scale-95"
27
+ >
28
+ <Upload className="w-5 h-5" />
29
+ Start Free Analysis
30
+ </Link>
31
+ <button className="inline-flex items-center justify-center gap-2 px-8 py-4 rounded-lg border border-slate-600 hover:border-blue-500 text-slate-200 font-semibold hover:bg-slate-800/50 transition-all">
32
+ Learn More
33
+ <ArrowRight className="w-4 h-4" />
34
+ </button>
35
+ </div>
36
+
37
+ <p className="text-sm text-slate-400">
38
+ Supports STEP, STP, and DXF files up to 50MB • 2-second analysis • No credit card required
39
+ </p>
40
+ </div>
41
+ </div>
42
+ </div>
43
+ </section>
44
+ );
45
+ }
components/landing/dashboard-preview.tsx ADDED
@@ -0,0 +1,232 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client";
2
+
3
+ import React from "react";
4
+ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
5
+ import { Badge } from "@/components/ui/badge";
6
+ import { AlertCircle, CheckCircle, FileText, Info, Wrench } from "lucide-react";
7
+ import { motion } from "framer-motion";
8
+
9
+ export function DashboardPreview() {
10
+ return (
11
+ <section id="dashboard" className="py-24 relative bg-gradient-to-b from-slate-900/30 to-slate-950/50 border-y border-border/50">
12
+ {/* Decorative blurs */}
13
+ <div className="absolute top-0 right-1/4 w-96 h-96 bg-cyan-500/10 rounded-full blur-3xl -z-10"></div>
14
+ <div className="absolute bottom-0 left-1/4 w-96 h-96 bg-blue-500/10 rounded-full blur-3xl -z-10"></div>
15
+
16
+ <div className="container mx-auto px-4 sm:px-6 lg:px-8">
17
+ <div className="text-center max-w-3xl mx-auto mb-16">
18
+ <motion.div
19
+ initial={{ opacity: 0, y: 20 }}
20
+ whileInView={{ opacity: 1, y: 0 }}
21
+ viewport={{ once: true }}
22
+ transition={{ duration: 0.5 }}
23
+ >
24
+ <h2 className="text-3xl font-bold tracking-tight text-foreground sm:text-4xl mb-4">
25
+ <span className="bg-gradient-to-r from-blue-300 via-cyan-300 to-blue-300 bg-clip-text text-transparent">
26
+ Enterprise Grade Analysis
27
+ </span>
28
+ </h2>
29
+ </motion.div>
30
+ <motion.p
31
+ className="text-lg text-muted-foreground"
32
+ initial={{ opacity: 0, y: 20 }}
33
+ whileInView={{ opacity: 1, y: 0 }}
34
+ viewport={{ once: true }}
35
+ transition={{ duration: 0.5, delay: 0.1 }}
36
+ >
37
+ A comprehensive dashboard providing deep insights into manufacturing feasibility, risk factors, and tool requirements.
38
+ </motion.p>
39
+ </div>
40
+
41
+ <motion.div
42
+ className="max-w-5xl mx-auto rounded-xl border border-cyan-500/20 bg-gradient-to-br from-slate-900/80 to-slate-950/80 shadow-2xl overflow-hidden backdrop-blur-sm"
43
+ initial={{ opacity: 0, y: 40, scale: 0.95 }}
44
+ whileInView={{ opacity: 1, y: 0, scale: 1 }}
45
+ viewport={{ once: true }}
46
+ transition={{ duration: 0.7 }}
47
+ >
48
+ {/* Dashboard Header with gradient */}
49
+ <div className="border-b border-cyan-500/20 bg-gradient-to-r from-slate-900/50 to-slate-950/50 px-6 py-4 flex items-center justify-between backdrop-blur-sm">
50
+ <div className="flex items-center gap-4">
51
+ <h3 className="font-semibold font-mono text-sm text-cyan-300">PART-A92-REV3</h3>
52
+ <Badge className="bg-gradient-to-r from-green-500 to-emerald-500 text-white border-0 rounded-sm shadow-lg">
53
+ <CheckCircle className="w-3 h-3 mr-1" /> Manufacturable
54
+ </Badge>
55
+ </div>
56
+ <div className="flex items-center gap-2">
57
+ <span className="text-xs text-cyan-400/70 font-mono">EST. CYCLE TIME: 4m 12s</span>
58
+ </div>
59
+ </div>
60
+
61
+ <div className="p-6 grid grid-cols-1 lg:grid-cols-3 gap-6">
62
+ {/* Left Column: Drawing Viewer & Agent Reasoning */}
63
+ <div className="lg:col-span-2 flex flex-col gap-6">
64
+ <Card className="flex-1 bg-gradient-to-br from-slate-850/60 to-slate-900/40 border-cyan-500/20 shadow-none">
65
+ <CardHeader className="py-4 px-5 border-b border-cyan-500/20">
66
+ <CardTitle className="text-sm flex items-center font-medium text-cyan-300">
67
+ <FileText className="w-4 h-4 mr-2 text-cyan-400" />
68
+ 3D model Analysis
69
+ </CardTitle>
70
+ </CardHeader>
71
+ <CardContent className="p-0 h-64 relative bg-card flex items-center justify-center overflow-hidden">
72
+ {/* Blueprint grid with gradient */}
73
+ <div className="absolute inset-0 opacity-[0.02] bg-[linear-gradient(to_right,#06b6d4_1px,transparent_1px),linear-gradient(to_bottom,#06b6d4_1px,transparent_1px)] bg-[size:20px_20px]"></div>
74
+ <div className="relative border-2 border-cyan-500/40 w-3/4 h-3/4 flex items-center justify-center rounded-lg bg-slate-950/50">
75
+ <span className="font-mono text-xs text-cyan-400/60">Interactive CAD Viewer</span>
76
+ <motion.div
77
+ className="absolute top-1/4 left-1/4 w-4 h-4 rounded-full border border-red-400/60 bg-red-500/20"
78
+ animate={{ scale: [1, 1.2, 1] }}
79
+ transition={{ duration: 2, repeat: Infinity }}
80
+ />
81
+ <div className="absolute top-1/2 right-1/3 w-4 h-4 rounded-full border border-yellow-400/60 bg-yellow-500/20"></div>
82
+ </div>
83
+ </CardContent>
84
+ </Card>
85
+
86
+ <Card className="bg-gradient-to-br from-slate-850/60 to-slate-900/40 border-cyan-500/20 shadow-none">
87
+ <CardHeader className="py-4 px-5 border-b border-cyan-500/20">
88
+ <CardTitle className="text-sm flex items-center font-medium text-cyan-300">
89
+ <BrainIcon className="w-4 h-4 mr-2 text-cyan-400" />
90
+ Agent Reasoning Panel
91
+ </CardTitle>
92
+ </CardHeader>
93
+ <CardContent className="p-4">
94
+ <div className="space-y-3 font-mono text-xs text-cyan-300/80">
95
+ <motion.div
96
+ className="flex gap-2"
97
+ initial={{ opacity: 0, x: -10 }}
98
+ whileInView={{ opacity: 1, x: 0 }}
99
+ viewport={{ once: true }}
100
+ transition={{ delay: 0 }}
101
+ >
102
+ <span className="text-cyan-400 flex-shrink-0">{">"}</span>
103
+ <p>Analyzed 14 geometric features and 8 dimensional tolerances.</p>
104
+ </motion.div>
105
+ <motion.div
106
+ className="flex gap-2"
107
+ initial={{ opacity: 0, x: -10 }}
108
+ whileInView={{ opacity: 1, x: 0 }}
109
+ viewport={{ once: true }}
110
+ transition={{ delay: 0.1 }}
111
+ >
112
+ <span className="text-cyan-400 flex-shrink-0">{">"}</span>
113
+ <p>Identified required operations: Facing, Roughing, Finishing, Tapping (M6x1.0).</p>
114
+ </motion.div>
115
+ <motion.div
116
+ className="flex gap-2"
117
+ initial={{ opacity: 0, x: -10 }}
118
+ whileInView={{ opacity: 1, x: 0 }}
119
+ viewport={{ once: true }}
120
+ transition={{ delay: 0.2 }}
121
+ >
122
+ <span className="text-cyan-400 flex-shrink-0">{">"}</span>
123
+ <p>Tolerance ±0.01mm on ID fits within machine HAAS-VF2 capability.</p>
124
+ </motion.div>
125
+ <motion.div
126
+ className="flex gap-2 text-green-400"
127
+ initial={{ opacity: 0, x: -10 }}
128
+ whileInView={{ opacity: 1, x: 0 }}
129
+ viewport={{ once: true }}
130
+ transition={{ delay: 0.3 }}
131
+ >
132
+ <span className="text-green-400 flex-shrink-0">{">"}</span>
133
+ <p>Conclusion: Part can be manufactured using standard shop tooling.</p>
134
+ </motion.div>
135
+ </div>
136
+ </CardContent>
137
+ </Card>
138
+ </div>
139
+
140
+ {/* Right Column: Risk Flags & Tools */}
141
+ <div className="flex flex-col gap-6">
142
+ <Card className="bg-gradient-to-br from-slate-850/60 to-slate-900/40 border-cyan-500/20 shadow-none">
143
+ <CardHeader className="py-4 px-5 border-b border-cyan-500/20">
144
+ <CardTitle className="text-sm flex items-center font-medium text-cyan-300">
145
+ <AlertCircle className="w-4 h-4 mr-2 text-cyan-400" />
146
+ Risk Flags
147
+ </CardTitle>
148
+ </CardHeader>
149
+ <CardContent className="p-4 space-y-3">
150
+ <div className="flex items-start gap-3 p-3 rounded-md bg-yellow-500/10 border border-yellow-500/30">
151
+ <AlertCircle className="w-4 h-4 text-yellow-400 mt-0.5 flex-shrink-0" />
152
+ <div>
153
+ <h4 className="text-xs font-semibold text-yellow-100">Deep Hole Drilling</h4>
154
+ <p className="text-[10px] text-yellow-200/70 mt-1">L/D ratio is 6:1. Peck drilling recommended.</p>
155
+ </div>
156
+ </div>
157
+ <div className="flex items-start gap-3 p-3 rounded-md bg-blue-500/10 border border-blue-500/30">
158
+ <Info className="w-4 h-4 text-blue-400 mt-0.5 flex-shrink-0" />
159
+ <div>
160
+ <h4 className="text-xs font-semibold text-blue-100">Thin Wall</h4>
161
+ <p className="text-[10px] text-blue-200/70 mt-1">Wall thickness 1.5mm. Watch for chatter.</p>
162
+ </div>
163
+ </div>
164
+ </CardContent>
165
+ </Card>
166
+
167
+ <Card className="flex-1 bg-gradient-to-br from-slate-850/60 to-slate-900/40 border-cyan-500/20 shadow-none">
168
+ <CardHeader className="py-4 px-5 border-b border-cyan-500/20">
169
+ <CardTitle className="text-sm flex items-center font-medium text-cyan-300">
170
+ <Wrench className="w-4 h-4 mr-2 text-cyan-400" />
171
+ Required Tools
172
+ </CardTitle>
173
+ </CardHeader>
174
+ <CardContent className="p-0">
175
+ <div className="divide-y divide-cyan-500/20 text-xs">
176
+ <div className="flex items-center justify-between p-3 px-5 hover:bg-cyan-500/10 transition-colors">
177
+ <span className="font-mono text-cyan-400/60">T01</span>
178
+ <span className="text-cyan-200">Face Mill 50mm</span>
179
+ <CheckCircle className="w-3 h-3 text-green-400" />
180
+ </div>
181
+ <div className="flex items-center justify-between p-3 px-5 hover:bg-cyan-500/10 transition-colors">
182
+ <span className="font-mono text-cyan-400/60">T04</span>
183
+ <span className="text-cyan-200">End Mill 10mm</span>
184
+ <CheckCircle className="w-3 h-3 text-green-400" />
185
+ </div>
186
+ <div className="flex items-center justify-between p-3 px-5 hover:bg-cyan-500/10 transition-colors">
187
+ <span className="font-mono text-cyan-400/60">T12</span>
188
+ <span className="text-cyan-200">Drill 5.0mm</span>
189
+ <CheckCircle className="w-3 h-3 text-green-400" />
190
+ </div>
191
+ <div className="flex items-center justify-between p-3 px-5 hover:bg-cyan-500/10 transition-colors">
192
+ <span className="font-mono text-cyan-400/60">T15</span>
193
+ <span className="text-cyan-200">Tap M6x1.0</span>
194
+ <CheckCircle className="w-3 h-3 text-green-400" />
195
+ </div>
196
+ </div>
197
+ </CardContent>
198
+ </Card>
199
+ </div>
200
+ </div>
201
+ </motion.div>
202
+ </div>
203
+ </section>
204
+ );
205
+ }
206
+
207
+ function BrainIcon(props: React.SVGProps<SVGSVGElement>) {
208
+ return (
209
+ <svg
210
+ {...props}
211
+ xmlns="http://www.w3.org/2000/svg"
212
+ width="24"
213
+ height="24"
214
+ viewBox="0 0 24 24"
215
+ fill="none"
216
+ stroke="currentColor"
217
+ strokeWidth="2"
218
+ strokeLinecap="round"
219
+ strokeLinejoin="round"
220
+ >
221
+ <path d="M12 5a3 3 0 1 0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z" />
222
+ <path d="M12 5a3 3 0 1 1 5.997.125 4 4 0 0 1 2.526 5.77 4 4 0 0 1-.556 6.588A4 4 0 1 1 12 18Z" />
223
+ <path d="M15 13a4.5 4.5 0 0 1-3-4 4.5 4.5 0 0 1-3 4" />
224
+ <path d="M17.599 6.5a3 3 0 0 0 .399-1.375" />
225
+ <path d="M6.002 5.125A3 3 0 0 0 6.401 6.5" />
226
+ <path d="M3.477 10.896a4 4 0 0 1 .585-.396" />
227
+ <path d="M19.938 10.5a4 4 0 0 1 .585.396" />
228
+ <path d="M6 18a4 4 0 0 1-1.967-.516" />
229
+ <path d="M19.967 17.484A4 4 0 0 1 18 18" />
230
+ </svg>
231
+ );
232
+ }
components/landing/features.tsx ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client";
2
+
3
+ import React from "react";
4
+ import { FileSearch, Settings2, Wrench, Cpu, AlertTriangle, FileText } from "lucide-react";
5
+ import { motion } from "framer-motion";
6
+
7
+ const features = [
8
+ {
9
+ title: "3D Parsing",
10
+ description: "Instantly parse complex 3D CAD models using AI agents.",
11
+ icon: FileSearch,
12
+ },
13
+ {
14
+ title: "CNC Operation Detection",
15
+ description: "Automatically identify required machining operations like turning, milling, drilling, and tapping.",
16
+ icon: Settings2,
17
+ },
18
+ {
19
+ title: "Tool Matching",
20
+ description: "Map detected features to available shop floor tooling to ensure manufacturability without custom tools.",
21
+ icon: Wrench,
22
+ },
23
+ {
24
+ title: "Machine Capability Analysis",
25
+ description: "Evaluate if part dimensions, tolerances, and operations fit within your specific machine envelope.",
26
+ icon: Cpu,
27
+ },
28
+ {
29
+ title: "Risk Detection",
30
+ description: "Identify high-risk geometric features, tight tolerances, or impossible-to-machine internal geometries.",
31
+ icon: AlertTriangle,
32
+ },
33
+ {
34
+ title: "Automated Reports",
35
+ description: "Generate comprehensive DFM (Design for Manufacturing) reports instantly for client feedback.",
36
+ icon: FileText,
37
+ },
38
+ ];
39
+
40
+ export function Features() {
41
+ return (
42
+ <section id="features" className="py-32 bg-gradient-to-b from-slate-900 via-slate-950 to-slate-900 relative overflow-hidden border-t border-slate-800">
43
+ <div className="absolute inset-0 overflow-hidden pointer-events-none">
44
+ <div className="absolute bottom-0 right-1/4 w-96 h-96 bg-cyan-600/10 rounded-full blur-3xl"></div>
45
+ </div>
46
+
47
+ <div className="container mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
48
+ <div className="text-center max-w-3xl mx-auto mb-20">
49
+ <h2 className="text-4xl sm:text-5xl font-bold tracking-tight mb-6 text-transparent bg-clip-text bg-gradient-to-r from-blue-300 to-cyan-300">
50
+ Industrial-Grade AI Capabilities
51
+ </h2>
52
+ <p className="text-lg text-slate-300 leading-relaxed">
53
+ Comprehensive manufacturability analysis tools designed for advanced machine shops
54
+ </p>
55
+ </div>
56
+
57
+ <div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
58
+ {features.map((feature, index) => {
59
+ const Icon = feature.icon;
60
+ return (
61
+ <motion.div
62
+ key={index}
63
+ initial={{ opacity: 0, y: 20 }}
64
+ whileInView={{ opacity: 1, y: 0 }}
65
+ viewport={{ once: true }}
66
+ transition={{ duration: 0.5, delay: index * 0.1 }}
67
+ >
68
+ <div className="group relative h-full">
69
+ <div className="absolute inset-0 bg-gradient-to-r from-blue-500/10 to-cyan-500/10 rounded-xl blur opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
70
+ <div className="relative bg-gradient-to-br from-slate-800/50 to-slate-900/50 border border-slate-700/50 group-hover:border-blue-500/50 rounded-xl p-8 transition-all duration-300 h-full">
71
+ <div className="h-12 w-12 rounded-lg bg-gradient-to-br from-blue-500/20 to-cyan-500/20 border border-blue-500/30 flex items-center justify-center mb-4 group-hover:from-blue-500/30 group-hover:to-cyan-500/30 transition-colors">
72
+ <Icon className="h-6 w-6 text-blue-400" />
73
+ </div>
74
+ <h3 className="text-xl font-semibold text-slate-100 mb-3">{feature.title}</h3>
75
+ <p className="text-slate-400 leading-relaxed">{feature.description}</p>
76
+ </div>
77
+ </div>
78
+ </motion.div>
79
+ );
80
+ })}
81
+ </div>
82
+ </div>
83
+ </section>
84
+ );
85
+ }
components/landing/footer.tsx ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from "react";
2
+ import Link from "next/link";
3
+ import { Layers } from "lucide-react";
4
+
5
+ export function Footer() {
6
+ return (
7
+ <footer className="border-t border-cyan-500/20 bg-gradient-to-b from-slate-950 to-slate-900 relative overflow-hidden">
8
+ {/* Decorative glow */}
9
+ <div className="absolute bottom-0 right-0 w-96 h-96 bg-cyan-500/5 rounded-full blur-3xl -z-10"></div>
10
+
11
+ <div className="container mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
12
+ <div className="grid grid-cols-1 md:grid-cols-4 gap-12 md:gap-8 py-16">
13
+ <div className="md:col-span-1">
14
+ <Link href="/" className="flex items-center space-x-2 mb-4 group">
15
+ <div className="p-2 rounded-lg bg-gradient-to-br from-blue-500 to-cyan-500 group-hover:shadow-lg group-hover:shadow-cyan-500/50 transition-shadow">
16
+ <Layers className="h-5 w-5 text-white" />
17
+ </div>
18
+ <span className="font-bold text-lg tracking-tight bg-gradient-to-r from-blue-300 to-cyan-300 bg-clip-text text-transparent">MachinaCheck</span>
19
+ </Link>
20
+ <p className="text-sm text-slate-400 leading-relaxed max-w-xs">
21
+ AI-powered CNC manufacturability assessment platform for advanced machine shops.
22
+ </p>
23
+ </div>
24
+
25
+ <div>
26
+ <h4 className="font-semibold text-slate-200 mb-4">Product</h4>
27
+ <ul className="space-y-3 text-sm text-slate-400">
28
+ <li><Link href="#" className="hover:text-cyan-300 transition-colors duration-200">Features</Link></li>
29
+ <li><Link href="#" className="hover:text-cyan-300 transition-colors duration-200">Workflow</Link></li>
30
+ <li><Link href="#" className="hover:text-cyan-300 transition-colors duration-200">Integrations</Link></li>
31
+ <li><Link href="#" className="hover:text-cyan-300 transition-colors duration-200">Pricing</Link></li>
32
+ </ul>
33
+ </div>
34
+
35
+ <div>
36
+ <h4 className="font-semibold text-slate-200 mb-4">Resources</h4>
37
+ <ul className="space-y-3 text-sm text-slate-400">
38
+ <li><Link href="#" className="hover:text-cyan-300 transition-colors duration-200">Documentation</Link></li>
39
+ <li><Link href="#" className="hover:text-cyan-300 transition-colors duration-200">API Reference</Link></li>
40
+ <li><Link href="#" className="hover:text-cyan-300 transition-colors duration-200">Blog</Link></li>
41
+ <li><Link href="#" className="hover:text-cyan-300 transition-colors duration-200">GitHub</Link></li>
42
+ </ul>
43
+ </div>
44
+
45
+ <div>
46
+ <h4 className="font-semibold text-slate-200 mb-4">Company</h4>
47
+ <ul className="space-y-3 text-sm text-slate-400">
48
+ <li><Link href="#" className="hover:text-cyan-300 transition-colors duration-200">About</Link></li>
49
+ <li><Link href="#" className="hover:text-cyan-300 transition-colors duration-200">Contact</Link></li>
50
+ <li><Link href="#" className="hover:text-cyan-300 transition-colors duration-200">Privacy Policy</Link></li>
51
+ <li><Link href="#" className="hover:text-cyan-300 transition-colors duration-200">Terms of Service</Link></li>
52
+ </ul>
53
+ </div>
54
+ </div>
55
+
56
+ <div className="py-8 border-t border-cyan-500/20 flex flex-col md:flex-row items-center justify-between text-xs text-slate-500">
57
+ <p>© {new Date().getFullYear()} MachinaCheck Inc. All rights reserved.</p>
58
+ <div className="flex space-x-6 mt-4 md:mt-0 font-mono">
59
+ <span className="text-cyan-400/60">SYS.STATUS: <span className="text-green-400">ONLINE</span></span>
60
+ <span className="text-slate-600">v2.0.4</span>
61
+ </div>
62
+ </div>
63
+ </div>
64
+ </footer>
65
+ );
66
+ }
components/landing/hero.tsx ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client";
2
+
3
+ import React from "react";
4
+ import Link from "next/link";
5
+ import { ArrowRight, Upload } from "lucide-react";
6
+ import { motion } from "framer-motion";
7
+
8
+ export function Hero() {
9
+ return (
10
+ <section className="relative overflow-hidden pt-32 pb-24 lg:pt-48 lg:pb-32 bg-gradient-to-b from-slate-900 via-slate-950 to-slate-900">
11
+ {/* Animated gradient blobs */}
12
+ <div className="absolute inset-0 overflow-hidden pointer-events-none">
13
+ <div className="absolute top-0 left-1/4 w-96 h-96 bg-blue-600/20 rounded-full blur-3xl animate-pulse"></div>
14
+ <div className="absolute top-1/3 right-0 w-80 h-80 bg-cyan-600/20 rounded-full blur-3xl animate-pulse delay-1000"></div>
15
+ </div>
16
+
17
+ <div className="container mx-auto px-4 sm:px-6 lg:px-8 relative z-10">
18
+ <div className="max-w-4xl mx-auto text-center">
19
+ <motion.div
20
+ initial={{ opacity: 0, y: 20 }}
21
+ animate={{ opacity: 1, y: 0 }}
22
+ transition={{ duration: 0.5 }}
23
+ >
24
+ <div className="inline-flex items-center gap-2 rounded-full border border-blue-500/30 bg-blue-950/50 px-4 py-2 mb-8">
25
+ <span className="flex h-2 w-2 rounded-full bg-blue-400 animate-pulse"></span>
26
+ <span className="text-sm font-medium text-blue-300">Powered by Multimodal AI</span>
27
+ </div>
28
+ </motion.div>
29
+
30
+ <motion.h1
31
+ className="text-5xl sm:text-6xl lg:text-7xl font-bold tracking-tight mb-6 text-transparent bg-clip-text bg-gradient-to-r from-blue-300 via-blue-200 to-cyan-300"
32
+ initial={{ opacity: 0, y: 20 }}
33
+ animate={{ opacity: 1, y: 0 }}
34
+ transition={{ duration: 0.5, delay: 0.1 }}
35
+ >
36
+ AI-Powered CNC<br />
37
+ Manufacturability
38
+ </motion.h1>
39
+
40
+ <motion.p
41
+ className="text-xl sm:text-2xl text-slate-300 mb-12 leading-relaxed max-w-2xl mx-auto"
42
+ initial={{ opacity: 0, y: 20 }}
43
+ animate={{ opacity: 1, y: 0 }}
44
+ transition={{ duration: 0.5, delay: 0.2 }}
45
+ >
46
+ Upload 3D model determine machining feasibility using AI agents
47
+ </motion.p>
48
+
49
+ <motion.div
50
+ className="flex flex-col sm:flex-row gap-4 justify-center"
51
+ initial={{ opacity: 0, y: 20 }}
52
+ animate={{ opacity: 1, y: 0 }}
53
+ transition={{ duration: 0.5, delay: 0.3 }}
54
+ >
55
+ <Link
56
+ href="/analyze"
57
+ className="inline-flex items-center justify-center gap-2 px-8 py-4 rounded-lg bg-gradient-to-r from-blue-600 to-cyan-500 hover:from-blue-700 hover:to-cyan-600 text-white font-semibold shadow-lg hover:shadow-blue-500/50 transition-all active:scale-95"
58
+ >
59
+ <Upload className="h-5 w-5" />
60
+ Start Analysis
61
+ </Link>
62
+ <button className="inline-flex items-center justify-center gap-2 px-8 py-4 rounded-lg border border-slate-600 hover:border-blue-500 text-slate-200 font-semibold transition-all hover:bg-slate-800/50">
63
+ View Documentation
64
+ <ArrowRight className="h-4 w-4" />
65
+ </button>
66
+ </motion.div>
67
+
68
+ <motion.div
69
+ className="mt-20 relative group"
70
+ initial={{ opacity: 0, scale: 0.95 }}
71
+ animate={{ opacity: 1, scale: 1 }}
72
+ transition={{ duration: 0.7, delay: 0.4 }}
73
+ >
74
+ <div className="absolute inset-0 bg-gradient-to-r from-blue-500/20 to-cyan-500/20 rounded-2xl blur-2xl group-hover:blur-3xl transition-all"></div>
75
+ <div className="relative bg-gradient-to-br from-slate-800/50 to-slate-900/50 border border-slate-700/50 rounded-2xl p-8 backdrop-blur-sm">
76
+ <div className="grid grid-cols-3 gap-6">
77
+ <div className="text-center">
78
+ <div className="text-3xl font-bold text-blue-400 mb-2">10,000+</div>
79
+ <div className="text-sm text-slate-400">Parts Analyzed</div>
80
+ </div>
81
+ <div className="text-center border-l border-r border-slate-700">
82
+ <div className="text-3xl font-bold text-cyan-400 mb-2">99.8%</div>
83
+ <div className="text-sm text-slate-400">Accuracy Rate</div>
84
+ </div>
85
+ <div className="text-center">
86
+ <div className="text-3xl font-bold text-blue-400 mb-2">&lt;2s</div>
87
+ <div className="text-sm text-slate-400">Analysis Time</div>
88
+ </div>
89
+ </div>
90
+ </div>
91
+ </motion.div>
92
+ </div>
93
+ </div>
94
+ </section>
95
+ );
96
+ }
components/landing/navbar.tsx ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from "react";
2
+ import Link from "next/link";
3
+ import { Layers } from "lucide-react";
4
+
5
+ export function Navbar() {
6
+ return (
7
+ <header className="sticky top-0 z-50 w-full border-b border-slate-800 bg-slate-950/80 backdrop-blur-xl supports-[backdrop-filter]:bg-slate-950/60">
8
+ <div className="container mx-auto px-4 sm:px-6 lg:px-8 flex h-16 items-center justify-between">
9
+ <Link href="/" className="flex items-center space-x-3 group">
10
+ <div className="p-2 rounded-lg bg-gradient-to-br from-blue-600 to-cyan-500 group-hover:from-blue-700 group-hover:to-cyan-600 transition-all">
11
+ <Layers className="h-5 w-5 text-white" />
12
+ </div>
13
+ <span className="font-bold text-lg tracking-tight text-white">MachinaCheck</span>
14
+ </Link>
15
+
16
+ <nav className="hidden md:flex items-center gap-8 text-sm font-medium">
17
+ <Link href="#features" className="text-slate-300 hover:text-blue-400 transition-colors">Features</Link>
18
+ <Link href="#workflow" className="text-slate-300 hover:text-blue-400 transition-colors">Workflow</Link>
19
+ <Link href="#dashboard" className="text-slate-300 hover:text-blue-400 transition-colors">Docs</Link>
20
+ </nav>
21
+
22
+ <Link
23
+ href="/analyze"
24
+ className="hidden sm:inline-flex px-6 py-2.5 bg-gradient-to-r from-blue-600 to-cyan-500 hover:from-blue-700 hover:to-cyan-600 text-white font-semibold text-sm rounded-lg transition-all shadow-lg hover:shadow-blue-500/50 active:scale-95"
25
+ >
26
+ Analyze Now
27
+ </Link>
28
+ </div>
29
+ </header>
30
+ );
31
+ }
components/landing/workflow.tsx ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client";
2
+
3
+ import React from "react";
4
+ import { Upload, BrainCircuit, Activity, Wrench, CheckCircle2, FileOutput } from "lucide-react";
5
+ import { motion } from "framer-motion";
6
+
7
+ const steps = [
8
+ { name: "Upload 3D model", icon: Upload, gradient: "from-blue-500 to-blue-600" },
9
+ { name: "AI Parsing", icon: BrainCircuit, gradient: "from-cyan-500 to-blue-500" },
10
+ { name: "Operations Detection", icon: Activity, gradient: "from-cyan-400 to-cyan-500" },
11
+ { name: "Tool Matching", icon: Wrench, gradient: "from-blue-400 to-cyan-400" },
12
+ { name: "Feasibility Decision", icon: CheckCircle2, gradient: "from-cyan-500 to-blue-500" },
13
+ { name: "Report Generation", icon: FileOutput, gradient: "from-blue-600 to-cyan-500" },
14
+ ];
15
+
16
+ export function Workflow() {
17
+ return (
18
+ <section id="workflow" className="py-24 relative bg-gradient-to-b from-slate-950/50 to-slate-900/30">
19
+ {/* Decorative blurs */}
20
+ <div className="absolute top-0 left-1/4 w-96 h-96 bg-blue-500/10 rounded-full blur-3xl -z-10"></div>
21
+ <div className="absolute bottom-0 right-1/4 w-96 h-96 bg-cyan-500/10 rounded-full blur-3xl -z-10"></div>
22
+
23
+ <div className="container mx-auto px-4 sm:px-6 lg:px-8">
24
+ <div className="text-center max-w-3xl mx-auto mb-20">
25
+ <motion.div
26
+ initial={{ opacity: 0, y: 20 }}
27
+ whileInView={{ opacity: 1, y: 0 }}
28
+ viewport={{ once: true }}
29
+ transition={{ duration: 0.5 }}
30
+ >
31
+ <h2 className="text-3xl font-bold tracking-tight text-foreground sm:text-4xl mb-4">
32
+ <span className="bg-gradient-to-r from-blue-300 via-cyan-300 to-blue-300 bg-clip-text text-transparent">
33
+ Automated Assessment Pipeline
34
+ </span>
35
+ </h2>
36
+ </motion.div>
37
+ <motion.p
38
+ className="text-lg text-muted-foreground"
39
+ initial={{ opacity: 0, y: 20 }}
40
+ whileInView={{ opacity: 1, y: 0 }}
41
+ viewport={{ once: true }}
42
+ transition={{ duration: 0.5, delay: 0.1 }}
43
+ >
44
+ A continuous, deterministic workflow that transforms raw CAD design into actionable manufacturing intelligence within seconds.
45
+ </motion.p>
46
+ </div>
47
+
48
+ <div className="relative max-w-5xl mx-auto">
49
+ {/* Animated connecting line */}
50
+ <motion.div
51
+ className="hidden lg:block absolute top-1/2 left-0 right-0 h-0.5 -translate-y-1/2 z-0"
52
+ initial={{ scaleX: 0, opacity: 0 }}
53
+ whileInView={{ scaleX: 1, opacity: 1 }}
54
+ viewport={{ once: true }}
55
+ transition={{ duration: 0.8, delay: 0.2 }}
56
+ style={{ transformOrigin: "left" }}
57
+ >
58
+ <div className="w-full h-full bg-gradient-to-r from-transparent via-blue-500/50 to-transparent"></div>
59
+ </motion.div>
60
+
61
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-6 gap-6 relative z-10">
62
+ {steps.map((step, index) => {
63
+ const Icon = step.icon;
64
+ return (
65
+ <motion.div
66
+ key={index}
67
+ className="flex flex-col items-center"
68
+ initial={{ opacity: 0, y: 20 }}
69
+ whileInView={{ opacity: 1, y: 0 }}
70
+ viewport={{ once: true }}
71
+ transition={{ duration: 0.5, delay: index * 0.1 }}
72
+ >
73
+ {/* Icon container with gradient glow */}
74
+ <motion.div
75
+ className={`h-16 w-16 rounded-xl bg-gradient-to-br ${step.gradient} flex items-center justify-center shadow-lg mb-4 relative z-10 cursor-pointer group`}
76
+ whileHover={{ scale: 1.1, boxShadow: "0 0 30px rgba(34, 211, 238, 0.5)" }}
77
+ transition={{ type: "spring", stiffness: 300, damping: 10 }}
78
+ >
79
+ <div className="absolute inset-0 rounded-xl bg-white/10 opacity-0 group-hover:opacity-20 transition-opacity"></div>
80
+ <Icon className="h-6 w-6 text-white relative z-10" />
81
+
82
+ {/* Glow effect on hover */}
83
+ <div className="absolute inset-0 rounded-xl bg-gradient-to-br opacity-0 group-hover:opacity-30 group-hover:blur-lg -z-10 transition-opacity" style={{ backgroundImage: `linear-gradient(to bottom right, rgba(34, 211, 238, 0.6), rgba(59, 130, 246, 0.6))` }}></div>
84
+ </motion.div>
85
+
86
+ <h3 className="text-sm font-semibold text-foreground text-center">
87
+ {step.name}
88
+ </h3>
89
+ <div className="text-xs text-muted-foreground mt-1 font-mono">
90
+ Step 0{index + 1}
91
+ </div>
92
+ </motion.div>
93
+ );
94
+ })}
95
+ </div>
96
+ </div>
97
+ </div>
98
+ </section>
99
+ );
100
+ }
components/ui/badge.tsx ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+ import { cva, type VariantProps } from "class-variance-authority"
3
+
4
+ import { cn } from "@/lib/utils"
5
+
6
+ const badgeVariants = cva(
7
+ "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default:
12
+ "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
13
+ secondary:
14
+ "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
15
+ destructive:
16
+ "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
17
+ outline: "text-foreground",
18
+ },
19
+ },
20
+ defaultVariants: {
21
+ variant: "default",
22
+ },
23
+ }
24
+ )
25
+
26
+ export interface BadgeProps
27
+ extends React.HTMLAttributes<HTMLDivElement>,
28
+ VariantProps<typeof badgeVariants> {}
29
+
30
+ function Badge({ className, variant, ...props }: BadgeProps) {
31
+ return (
32
+ <div className={cn(badgeVariants({ variant }), className)} {...props} />
33
+ )
34
+ }
35
+
36
+ export { Badge, badgeVariants }
components/ui/button.tsx ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+ import { cva, type VariantProps } from "class-variance-authority"
3
+ import { Slot } from "radix-ui"
4
+
5
+ import { cn } from "@/lib/utils"
6
+
7
+ const buttonVariants = cva(
8
+ "group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
13
+ outline:
14
+ "border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
15
+ secondary:
16
+ "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
17
+ ghost:
18
+ "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
19
+ destructive:
20
+ "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
21
+ link: "text-primary underline-offset-4 hover:underline",
22
+ },
23
+ size: {
24
+ default:
25
+ "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
26
+ xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
27
+ sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
28
+ lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
29
+ icon: "size-8",
30
+ "icon-xs":
31
+ "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
32
+ "icon-sm":
33
+ "size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
34
+ "icon-lg": "size-9",
35
+ },
36
+ },
37
+ defaultVariants: {
38
+ variant: "default",
39
+ size: "default",
40
+ },
41
+ }
42
+ )
43
+
44
+ function Button({
45
+ className,
46
+ variant = "default",
47
+ size = "default",
48
+ asChild = false,
49
+ ...props
50
+ }: React.ComponentProps<"button"> &
51
+ VariantProps<typeof buttonVariants> & {
52
+ asChild?: boolean
53
+ }) {
54
+ const Comp = asChild ? Slot.Root : "button"
55
+
56
+ return (
57
+ <Comp
58
+ data-slot="button"
59
+ data-variant={variant}
60
+ data-size={size}
61
+ className={cn(buttonVariants({ variant, size, className }))}
62
+ {...props}
63
+ />
64
+ )
65
+ }
66
+
67
+ export { Button, buttonVariants }
components/ui/card.tsx ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+
3
+ import { cn } from "@/lib/utils"
4
+
5
+ const Card = React.forwardRef<
6
+ HTMLDivElement,
7
+ React.HTMLAttributes<HTMLDivElement>
8
+ >(({ className, ...props }, ref) => (
9
+ <div
10
+ ref={ref}
11
+ className={cn(
12
+ "rounded-xl border bg-card text-card-foreground shadow",
13
+ className
14
+ )}
15
+ {...props}
16
+ />
17
+ ))
18
+ Card.displayName = "Card"
19
+
20
+ const CardHeader = React.forwardRef<
21
+ HTMLDivElement,
22
+ React.HTMLAttributes<HTMLDivElement>
23
+ >(({ className, ...props }, ref) => (
24
+ <div
25
+ ref={ref}
26
+ className={cn("flex flex-col space-y-1.5 p-6", className)}
27
+ {...props}
28
+ />
29
+ ))
30
+ CardHeader.displayName = "CardHeader"
31
+
32
+ const CardTitle = React.forwardRef<
33
+ HTMLParagraphElement,
34
+ React.HTMLAttributes<HTMLHeadingElement>
35
+ >(({ className, ...props }, ref) => (
36
+ <h3
37
+ ref={ref}
38
+ className={cn("font-semibold leading-none tracking-tight", className)}
39
+ {...props}
40
+ />
41
+ ))
42
+ CardTitle.displayName = "CardTitle"
43
+
44
+ const CardDescription = React.forwardRef<
45
+ HTMLParagraphElement,
46
+ React.HTMLAttributes<HTMLParagraphElement>
47
+ >(({ className, ...props }, ref) => (
48
+ <p
49
+ ref={ref}
50
+ className={cn("text-sm text-muted-foreground", className)}
51
+ {...props}
52
+ />
53
+ ))
54
+ CardDescription.displayName = "CardDescription"
55
+
56
+ const CardContent = React.forwardRef<
57
+ HTMLDivElement,
58
+ React.HTMLAttributes<HTMLDivElement>
59
+ >(({ className, ...props }, ref) => (
60
+ <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
61
+ ))
62
+ CardContent.displayName = "CardContent"
63
+
64
+ const CardFooter = React.forwardRef<
65
+ HTMLDivElement,
66
+ React.HTMLAttributes<HTMLDivElement>
67
+ >(({ className, ...props }, ref) => (
68
+ <div
69
+ ref={ref}
70
+ className={cn("flex items-center p-6 pt-0", className)}
71
+ {...props}
72
+ />
73
+ ))
74
+ CardFooter.displayName = "CardFooter"
75
+
76
+ export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
components/ui/input.tsx ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+
3
+ import { cn } from "@/lib/utils"
4
+
5
+ const Input = React.forwardRef<
6
+ HTMLInputElement,
7
+ React.ComponentProps<"input">
8
+ >(({ className, type, ...props }, ref) => (
9
+ <input
10
+ type={type}
11
+ className={cn(
12
+ "flex h-9 w-full rounded-lg border border-input bg-transparent px-3 py-1 text-base shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm pointer-events-auto cursor-text",
13
+ className
14
+ )}
15
+ ref={ref}
16
+ {...props}
17
+ />
18
+ ))
19
+ Input.displayName = "Input"
20
+
21
+ export { Input }
components/ui/label.tsx ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+ import * as LabelPrimitive from "@radix-ui/react-label"
3
+ import { cva, type VariantProps } from "class-variance-authority"
4
+
5
+ import { cn } from "@/lib/utils"
6
+
7
+ const labelVariants = cva(
8
+ "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
9
+ )
10
+
11
+ const Label = React.forwardRef<
12
+ React.ElementRef<typeof LabelPrimitive.Root>,
13
+ React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
14
+ VariantProps<typeof labelVariants>
15
+ >(({ className, ...props }, ref) => (
16
+ <LabelPrimitive.Root
17
+ ref={ref}
18
+ className={cn(labelVariants(), className)}
19
+ {...props}
20
+ />
21
+ ))
22
+ Label.displayName = LabelPrimitive.Root.displayName
23
+
24
+ export { Label }
dockerfile ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM node:18-alpine
2
+ WORKDIR /app
3
+ COPY package*.json ./
4
+ RUN npm install
5
+ COPY . .
6
+ ENV NEXT_PUBLIC_API_URL=https://your-ngrok-url.ngrok-free.app
7
+ RUN npm run build
8
+ EXPOSE 3000
9
+ ENV PORT 3000
10
+ CMD ["npm", "start"]
eslint.config.mjs ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { defineConfig, globalIgnores } from "eslint/config";
2
+ import nextVitals from "eslint-config-next/core-web-vitals";
3
+ import nextTs from "eslint-config-next/typescript";
4
+
5
+ const eslintConfig = defineConfig([
6
+ ...nextVitals,
7
+ ...nextTs,
8
+ // Override default ignores of eslint-config-next.
9
+ globalIgnores([
10
+ // Default ignores of eslint-config-next:
11
+ ".next/**",
12
+ "out/**",
13
+ "build/**",
14
+ "next-env.d.ts",
15
+ ]),
16
+ ]);
17
+
18
+ export default eslintConfig;
hooks/use-analyze.ts ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client";
2
+
3
+ import { useState, useCallback } from "react";
4
+ import { useForm } from "react-hook-form";
5
+ import { zodResolver } from "@hookform/resolvers/zod";
6
+ import { z } from "zod";
7
+ import { analyzeStep } from "@/lib/api";
8
+ import { AnalysisResponse, AnalyzeFormInputs } from "@/lib/types";
9
+
10
+ const AnalyzeFormSchema = z.object({
11
+ material: z.string().min(1, "Material is required"),
12
+ tolerance: z.string().min(1, "Tolerance is required"),
13
+ threads: z.string(),
14
+ });
15
+
16
+ type FormSchemaType = z.infer<typeof AnalyzeFormSchema>;
17
+
18
+ export function useAnalyze() {
19
+ const [selectedFile, setSelectedFile] = useState<File | null>(null);
20
+ const [result, setResult] = useState<AnalysisResponse | null>(null);
21
+ const [error, setError] = useState<string | null>(null);
22
+ const [isLoading, setIsLoading] = useState(false);
23
+
24
+ const form = useForm<FormSchemaType>({
25
+ resolver: zodResolver(AnalyzeFormSchema),
26
+ defaultValues: {
27
+ material: "",
28
+ tolerance: "",
29
+ threads: "",
30
+ },
31
+ });
32
+
33
+ const handleFileSelect = useCallback((file: File) => {
34
+ const validExtensions = [".step", ".stp"];
35
+ const hasValidExtension = validExtensions.some((ext) =>
36
+ file.name.toLowerCase().endsWith(ext)
37
+ );
38
+
39
+ if (!hasValidExtension) {
40
+ setError("Invalid file type. Please upload a .step or .stp file.");
41
+ setSelectedFile(null);
42
+ return;
43
+ }
44
+
45
+ setSelectedFile(file);
46
+ setError(null);
47
+ }, []);
48
+
49
+ const handleFileRemove = useCallback(() => {
50
+ setSelectedFile(null);
51
+ setError(null);
52
+ }, []);
53
+
54
+ const onSubmit = async (data: FormSchemaType) => {
55
+ if (!selectedFile) {
56
+ setError("Please select a STEP file to analyze.");
57
+ return;
58
+ }
59
+
60
+ setIsLoading(true);
61
+ setError(null);
62
+ setResult(null);
63
+
64
+ try {
65
+ const formData = new FormData();
66
+ formData.append("file", selectedFile);
67
+ formData.append("material", data.material);
68
+ formData.append("tolerance", data.tolerance);
69
+ if (data.threads) {
70
+ formData.append("threads", data.threads);
71
+ }
72
+
73
+ const response = await analyzeStep(formData);
74
+ setResult(response);
75
+ } catch (err) {
76
+ const message =
77
+ err instanceof Error ? err.message : "Failed to analyze file";
78
+ setError(message);
79
+ setResult(null);
80
+ } finally {
81
+ setIsLoading(false);
82
+ }
83
+ };
84
+
85
+ const resetAnalysis = useCallback(() => {
86
+ setSelectedFile(null);
87
+ setResult(null);
88
+ setError(null);
89
+ form.reset();
90
+ }, [form]);
91
+
92
+ return {
93
+ form,
94
+ selectedFile,
95
+ handleFileSelect,
96
+ handleFileRemove,
97
+ onSubmit: form.handleSubmit(onSubmit),
98
+ result,
99
+ error,
100
+ isLoading,
101
+ resetAnalysis,
102
+ };
103
+ }
lib/api.ts ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import axios, { AxiosInstance } from "axios";
2
+ import { AnalysisResponse, DecisionType } from "./types";
3
+
4
+ const API_URL = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8080";
5
+
6
+ const client: AxiosInstance = axios.create({
7
+ baseURL: API_URL,
8
+ timeout: 120000,
9
+ headers: {
10
+ "Content-Type": "multipart/form-data",
11
+ },
12
+ });
13
+
14
+ function formatTool(t: { type: string; diameter_mm?: number; min_diameter_mm?: number; material?: string }): string {
15
+ const size = t.diameter_mm ? `${t.diameter_mm}mm` : t.min_diameter_mm ? `min ${t.min_diameter_mm}mm` : "";
16
+ return [t.type, size, t.material].filter(Boolean).join(" ");
17
+ }
18
+
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ function transformResponse(raw: any): AnalysisResponse {
21
+ const decisionStr: DecisionType =
22
+ typeof raw.decision === "object" ? raw.decision?.decision : raw.decision;
23
+
24
+ return {
25
+ decision: decisionStr,
26
+ summary: raw.report?.summary ?? raw.summary ?? "",
27
+ operations: raw.classification?.required_operations ?? raw.operations ?? [],
28
+ required_tools: (raw.match_result?.tools_available ?? raw.classification?.required_tools ?? []).map(formatTool),
29
+ missing_tools: (raw.match_result?.tools_missing ?? raw.missing_tools ?? []).map(formatTool),
30
+ recommendations: raw.report?.action_required ?? raw.recommendations ?? [],
31
+ status: raw.status,
32
+ features: raw.features,
33
+ classification: raw.classification,
34
+ match_result: raw.match_result,
35
+ };
36
+ }
37
+
38
+ export async function analyzeStep(formData: FormData): Promise<AnalysisResponse> {
39
+ try {
40
+ const response = await client.post("/analyze", formData);
41
+ return transformResponse(response.data);
42
+ } catch (error) {
43
+ if (axios.isAxiosError(error)) {
44
+ const message =
45
+ error.response?.data?.detail ||
46
+ error.response?.data?.message ||
47
+ error.message ||
48
+ "Failed to analyze file. Please try again.";
49
+ throw new Error(message);
50
+ }
51
+ throw error;
52
+ }
53
+ }
lib/types.ts ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export type DecisionType = "YES" | "NO" | "CONDITIONAL";
2
+
3
+ export interface AnalysisResponse {
4
+ decision: DecisionType;
5
+ summary: string;
6
+ operations: string[];
7
+ required_tools: string[];
8
+ missing_tools?: string[];
9
+ recommendations?: string[];
10
+ status: string;
11
+ features?: Record<string, unknown>;
12
+ classification?: Record<string, unknown>;
13
+ match_result?: Record<string, unknown>;
14
+ }
15
+
16
+ export interface AnalyzeFormInputs {
17
+ material: string;
18
+ tolerance: string;
19
+ threads?: string;
20
+ }
lib/utils.ts ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ import { clsx, type ClassValue } from "clsx"
2
+ import { twMerge } from "tailwind-merge"
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs))
6
+ }
next.config.js ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ /** @type {import('next').NextConfig} */
2
+ const nextConfig = {
3
+ output: 'standalone',
4
+ }
5
+ module.exports = nextConfig
next.config.ts ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ import type { NextConfig } from "next";
2
+
3
+ const nextConfig: NextConfig = {
4
+ output: 'standalone',
5
+ };
6
+
7
+ export default nextConfig;
package-lock.json ADDED
The diff for this file is too large to render. See raw diff
 
package.json ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "frontend",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "next dev",
7
+ "build": "next build",
8
+ "start": "next start",
9
+ "lint": "eslint"
10
+ },
11
+ "dependencies": {
12
+ "@hookform/resolvers": "^5.2.2",
13
+ "@tanstack/react-query": "^5.100.9",
14
+ "axios": "^1.16.0",
15
+ "class-variance-authority": "^0.7.1",
16
+ "clsx": "^2.1.1",
17
+ "framer-motion": "^12.38.0",
18
+ "lucide-react": "^1.14.0",
19
+ "next": "16.2.6",
20
+ "radix-ui": "^1.4.3",
21
+ "react": "19.2.4",
22
+ "react-dom": "19.2.4",
23
+ "react-hook-form": "^7.75.0",
24
+ "shadcn": "^4.7.0",
25
+ "tailwind-merge": "^3.5.0",
26
+ "tw-animate-css": "^1.4.0",
27
+ "zod": "^4.4.3",
28
+ "zustand": "^5.0.13"
29
+ },
30
+ "devDependencies": {
31
+ "@tailwindcss/postcss": "^4",
32
+ "@types/node": "^20",
33
+ "@types/react": "^19",
34
+ "@types/react-dom": "^19",
35
+ "eslint": "^9",
36
+ "eslint-config-next": "16.2.6",
37
+ "tailwindcss": "^4",
38
+ "typescript": "^5"
39
+ }
40
+ }
postcss.config.mjs ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ const config = {
2
+ plugins: {
3
+ "@tailwindcss/postcss": {},
4
+ },
5
+ };
6
+
7
+ export default config;
public/file.svg ADDED
public/globe.svg ADDED
public/next.svg ADDED
public/vercel.svg ADDED
public/window.svg ADDED
tsconfig.json ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2017",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "allowJs": true,
6
+ "skipLibCheck": true,
7
+ "strict": true,
8
+ "noEmit": true,
9
+ "esModuleInterop": true,
10
+ "module": "esnext",
11
+ "moduleResolution": "bundler",
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "jsx": "react-jsx",
15
+ "incremental": true,
16
+ "plugins": [
17
+ {
18
+ "name": "next"
19
+ }
20
+ ],
21
+ "paths": {
22
+ "@/*": ["./*"]
23
+ }
24
+ },
25
+ "include": [
26
+ "next-env.d.ts",
27
+ "**/*.ts",
28
+ "**/*.tsx",
29
+ ".next/types/**/*.ts",
30
+ ".next/dev/types/**/*.ts",
31
+ "**/*.mts"
32
+ ],
33
+ "exclude": ["node_modules"]
34
+ }