@rudderhq/agent-runtime-opencode-local 0.7.2-canary.0 → 0.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/models.d.ts.map +1 -1
- package/dist/server/models.js +86 -4
- package/dist/server/models.js.map +1 -1
- package/dist/server/models.test.js +31 -0
- package/dist/server/models.test.js.map +1 -1
- package/package.json +2 -2
- package/skills/app-builder/SKILL.md +39 -9
- package/skills/app-builder/assets/scaffold/app/globals.css +109 -16
- package/skills/app-builder/assets/scaffold/components/contacts-workspace.tsx +176 -152
- package/skills/app-builder/assets/scaffold/components/ui/alert.tsx +12 -0
- package/skills/app-builder/assets/scaffold/components/ui/badge.tsx +27 -0
- package/skills/app-builder/assets/scaffold/components/ui/button.tsx +9 -7
- package/skills/app-builder/assets/scaffold/components/ui/card.tsx +15 -3
- package/skills/app-builder/assets/scaffold/components/ui/empty.tsx +18 -0
- package/skills/app-builder/assets/scaffold/components/ui/field.tsx +23 -0
- package/skills/app-builder/assets/scaffold/components/ui/input.tsx +1 -1
- package/skills/app-builder/assets/scaffold/components/ui/label.tsx +1 -1
- package/skills/app-builder/assets/scaffold/components/ui/separator.tsx +6 -0
- package/skills/app-builder/assets/scaffold/components/ui/skeleton.tsx +6 -0
- package/skills/app-builder/assets/scaffold/components/ui/table.tsx +30 -0
- package/skills/app-builder/assets/scaffold/components.json +20 -0
- package/skills/app-builder/assets/scaffold/next-env.d.ts +1 -1
- package/skills/app-builder/assets/scaffold/next.config.ts +19 -0
- package/skills/app-builder/assets/scaffold/package.json +2 -1
- package/skills/app-builder/assets/scaffold/rudder.app.json +1 -1
- package/skills/app-builder/assets/scaffold/rudder.ui.json +13 -0
- package/skills/app-builder/assets/scaffold/scripts/validate-rudder-ui.mjs +50 -0
- package/skills/app-builder/assets/scaffold/tests/e2e/app.spec.ts +35 -1
- package/skills/app-builder/evals/evals.json +2 -1
- package/skills/app-builder/references/design-guidelines.md +19 -2
- package/skills/app-builder/references/rudder-ui-preset.md +73 -0
- package/skills/app-builder/references/scaffold-contract.md +12 -0
- package/skills/app-builder/references/verification.md +6 -0
- package/skills/app-builder/scripts/report-build-status.mjs +83 -0
- package/skills/browser/SKILL.md +11 -4
- package/skills/browser/agents/openai.yaml +2 -2
- package/skills/browser/evals/trigger-evals.json +22 -0
- package/skills/rudder-docs/SKILL.md +2 -2
- package/skills/rudder-docs/evals/retrieval-authority-evals.json +40 -0
- package/skills/rudder-docs/evals/trigger-evals.json +2 -2
- package/skills/rudder-docs/references/api-reference.md +0 -2
- package/skills/rudder-docs/references/plugin-authoring.md +38 -114
- package/skills/rudder-docs/references/source-map.md +2 -0
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { Alert } from "@/components/ui/alert";
|
|
4
|
+
import { Badge } from "@/components/ui/badge";
|
|
3
5
|
import { Button } from "@/components/ui/button";
|
|
4
|
-
import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
|
6
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
|
7
|
+
import { Empty, EmptyDescription, EmptyHeader, EmptyTitle } from "@/components/ui/empty";
|
|
8
|
+
import { Field, FieldGroup, FieldLabel } from "@/components/ui/field";
|
|
5
9
|
import { Input } from "@/components/ui/input";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
10
|
+
import { Skeleton } from "@/components/ui/skeleton";
|
|
11
|
+
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
|
12
|
+
import { Download, Plus, Search, Trash2, Upload, UsersRound } from "lucide-react";
|
|
8
13
|
import { FormEvent, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
9
14
|
|
|
10
15
|
type Contact = {
|
|
@@ -25,6 +30,13 @@ type Draft = {
|
|
|
25
30
|
|
|
26
31
|
const emptyDraft: Draft = { name: "", email: "", company: "" };
|
|
27
32
|
|
|
33
|
+
const statusVariant: Record<Contact["status"], "secondary" | "outline" | "success"> = {
|
|
34
|
+
new: "secondary",
|
|
35
|
+
contacted: "outline",
|
|
36
|
+
replied: "success",
|
|
37
|
+
paused: "outline",
|
|
38
|
+
};
|
|
39
|
+
|
|
28
40
|
export function ContactsWorkspace() {
|
|
29
41
|
const [contacts, setContacts] = useState<Contact[]>([]);
|
|
30
42
|
const [draft, setDraft] = useState<Draft>(emptyDraft);
|
|
@@ -118,175 +130,187 @@ export function ContactsWorkspace() {
|
|
|
118
130
|
}
|
|
119
131
|
|
|
120
132
|
return (
|
|
121
|
-
<div className="
|
|
122
|
-
<
|
|
123
|
-
<
|
|
124
|
-
<div>
|
|
125
|
-
<
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
<input
|
|
133
|
-
ref={importRef}
|
|
134
|
-
aria-label="Import app data"
|
|
135
|
-
className="sr-only"
|
|
136
|
-
type="file"
|
|
137
|
-
accept="application/json,.json"
|
|
138
|
-
onChange={(event) => {
|
|
139
|
-
const file = event.currentTarget.files?.[0];
|
|
140
|
-
if (file) void importData(file);
|
|
141
|
-
}}
|
|
142
|
-
/>
|
|
143
|
-
<Button type="button" variant="outline" onClick={() => importRef.current?.click()}>
|
|
144
|
-
<Upload aria-hidden size={16} />
|
|
145
|
-
Import
|
|
146
|
-
</Button>
|
|
147
|
-
<Button asChild variant="outline">
|
|
148
|
-
<a href="/api/data/export" download>
|
|
149
|
-
<Download aria-hidden size={16} />
|
|
150
|
-
Export
|
|
151
|
-
</a>
|
|
152
|
-
</Button>
|
|
133
|
+
<div className="min-h-screen bg-background text-foreground">
|
|
134
|
+
<header className="border-b bg-card/80">
|
|
135
|
+
<div className="mx-auto flex h-14 max-w-7xl items-center justify-between px-4 lg:px-6">
|
|
136
|
+
<div className="flex min-w-0 items-center gap-3">
|
|
137
|
+
<span className="flex size-8 shrink-0 items-center justify-center rounded-md bg-primary text-primary-foreground">
|
|
138
|
+
<UsersRound aria-hidden className="size-4" />
|
|
139
|
+
</span>
|
|
140
|
+
<div className="min-w-0">
|
|
141
|
+
<p className="m-0 truncate text-sm font-semibold">Pipeline Desk</p>
|
|
142
|
+
<p className="m-0 text-xs text-muted-foreground">Local customer workspace</p>
|
|
143
|
+
</div>
|
|
153
144
|
</div>
|
|
154
|
-
|
|
145
|
+
<Badge variant="outline">Development data</Badge>
|
|
146
|
+
</div>
|
|
147
|
+
</header>
|
|
155
148
|
|
|
156
|
-
|
|
157
|
-
|
|
149
|
+
<main className="mx-auto grid max-w-7xl gap-4 px-4 py-5 lg:grid-cols-[minmax(0,1fr)_20rem] lg:px-6">
|
|
150
|
+
<section className="min-w-0">
|
|
151
|
+
<div className="mb-4 flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between">
|
|
158
152
|
<div>
|
|
159
|
-
<
|
|
160
|
-
<p className="mb-0 mt-1 text-sm text-
|
|
161
|
-
|
|
153
|
+
<h1 className="m-0 text-2xl font-semibold">Contacts</h1>
|
|
154
|
+
<p className="mb-0 mt-1 text-sm text-muted-foreground">
|
|
155
|
+
Keep the next conversation visible and every record on this device.
|
|
162
156
|
</p>
|
|
163
157
|
</div>
|
|
164
|
-
<div className="
|
|
165
|
-
<
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
onChange={(event) => setQuery(event.target.value)}
|
|
158
|
+
<div className="flex flex-wrap gap-2">
|
|
159
|
+
<input
|
|
160
|
+
ref={importRef}
|
|
161
|
+
aria-label="Import app data"
|
|
162
|
+
className="sr-only"
|
|
163
|
+
type="file"
|
|
164
|
+
accept="application/json,.json"
|
|
165
|
+
onChange={(event) => {
|
|
166
|
+
const file = event.currentTarget.files?.[0];
|
|
167
|
+
if (file) void importData(file);
|
|
168
|
+
}}
|
|
176
169
|
/>
|
|
170
|
+
<Button type="button" variant="outline" onClick={() => importRef.current?.click()}>
|
|
171
|
+
<Upload aria-hidden data-icon="inline-start" />
|
|
172
|
+
Import
|
|
173
|
+
</Button>
|
|
174
|
+
<Button asChild variant="outline">
|
|
175
|
+
<a href="/api/data/export" download>
|
|
176
|
+
<Download aria-hidden data-icon="inline-start" />
|
|
177
|
+
Export
|
|
178
|
+
</a>
|
|
179
|
+
</Button>
|
|
177
180
|
</div>
|
|
178
|
-
</
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
181
|
+
</div>
|
|
182
|
+
|
|
183
|
+
<Card>
|
|
184
|
+
<CardHeader className="sm:flex-row sm:items-center sm:justify-between">
|
|
185
|
+
<div>
|
|
186
|
+
<CardTitle>All contacts</CardTitle>
|
|
187
|
+
<CardDescription>{contacts.length} {contacts.length === 1 ? "record" : "records"}</CardDescription>
|
|
188
|
+
</div>
|
|
189
|
+
<div className="relative w-full sm:w-64">
|
|
190
|
+
<Search aria-hidden className="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
|
|
191
|
+
<Input
|
|
192
|
+
aria-label="Search contacts"
|
|
193
|
+
className="pl-9"
|
|
194
|
+
placeholder="Search contacts"
|
|
195
|
+
value={query}
|
|
196
|
+
onChange={(event) => setQuery(event.target.value)}
|
|
197
|
+
/>
|
|
194
198
|
</div>
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
</
|
|
205
|
-
|
|
206
|
-
|
|
199
|
+
</CardHeader>
|
|
200
|
+
<div aria-live="polite">
|
|
201
|
+
{loading ? (
|
|
202
|
+
<div className="flex flex-col gap-3 p-4" aria-label="Loading contacts">
|
|
203
|
+
{[0, 1, 2].map((item) => <Skeleton className="h-11 w-full" key={item} />)}
|
|
204
|
+
</div>
|
|
205
|
+
) : visibleContacts.length === 0 ? (
|
|
206
|
+
<Empty>
|
|
207
|
+
<EmptyHeader>
|
|
208
|
+
<EmptyTitle>{contacts.length === 0 ? "No contacts yet" : "No matching contacts"}</EmptyTitle>
|
|
209
|
+
<EmptyDescription>
|
|
210
|
+
{contacts.length === 0
|
|
211
|
+
? "Add the first contact with the form beside this list."
|
|
212
|
+
: "Try a different name, email, company, or status."}
|
|
213
|
+
</EmptyDescription>
|
|
214
|
+
</EmptyHeader>
|
|
215
|
+
</Empty>
|
|
216
|
+
) : (
|
|
217
|
+
<Table className="table-fixed sm:min-w-[40rem] sm:table-auto">
|
|
218
|
+
<TableHeader>
|
|
219
|
+
<TableRow>
|
|
220
|
+
<TableHead scope="col">Contact</TableHead>
|
|
221
|
+
<TableHead className="hidden sm:table-cell" scope="col">Company</TableHead>
|
|
222
|
+
<TableHead className="w-20 sm:w-auto" scope="col">Status</TableHead>
|
|
223
|
+
<TableHead aria-label="Actions" className="w-12 text-right sm:w-auto" scope="col">
|
|
224
|
+
<span className="hidden sm:inline">Actions</span>
|
|
225
|
+
</TableHead>
|
|
226
|
+
</TableRow>
|
|
227
|
+
</TableHeader>
|
|
228
|
+
<TableBody>
|
|
207
229
|
{visibleContacts.map((contact) => (
|
|
208
|
-
<
|
|
209
|
-
<
|
|
230
|
+
<TableRow key={contact.id}>
|
|
231
|
+
<TableCell>
|
|
210
232
|
<strong className="block font-medium">{contact.name}</strong>
|
|
211
|
-
<span className="text-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
<td className="px-5 py-4">
|
|
215
|
-
<span className="rounded-full bg-[var(--muted)] px-2.5 py-1 text-xs font-medium capitalize">
|
|
216
|
-
{contact.status}
|
|
233
|
+
<span className="block break-all text-muted-foreground sm:inline">{contact.email}</span>
|
|
234
|
+
<span className="mt-1 block text-xs text-muted-foreground sm:hidden">
|
|
235
|
+
{contact.company || "Company not set"}
|
|
217
236
|
</span>
|
|
218
|
-
</
|
|
219
|
-
<
|
|
237
|
+
</TableCell>
|
|
238
|
+
<TableCell className="hidden sm:table-cell">{contact.company || "Not set"}</TableCell>
|
|
239
|
+
<TableCell>
|
|
240
|
+
<Badge variant={statusVariant[contact.status]}>{contact.status}</Badge>
|
|
241
|
+
</TableCell>
|
|
242
|
+
<TableCell className="text-right">
|
|
220
243
|
<Button
|
|
221
244
|
aria-label={`Delete ${contact.name}`}
|
|
222
|
-
size="sm"
|
|
245
|
+
size="icon-sm"
|
|
223
246
|
type="button"
|
|
224
247
|
variant="ghost"
|
|
225
248
|
onClick={() => void deleteContact(contact)}
|
|
226
249
|
>
|
|
227
|
-
<Trash2 aria-hidden
|
|
250
|
+
<Trash2 aria-hidden />
|
|
228
251
|
</Button>
|
|
229
|
-
</
|
|
230
|
-
</
|
|
252
|
+
</TableCell>
|
|
253
|
+
</TableRow>
|
|
231
254
|
))}
|
|
232
|
-
</
|
|
233
|
-
</
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
</
|
|
237
|
-
</
|
|
238
|
-
</section>
|
|
255
|
+
</TableBody>
|
|
256
|
+
</Table>
|
|
257
|
+
)}
|
|
258
|
+
</div>
|
|
259
|
+
</Card>
|
|
260
|
+
</section>
|
|
239
261
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
<
|
|
250
|
-
<
|
|
251
|
-
<
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
262
|
+
<aside>
|
|
263
|
+
<Card>
|
|
264
|
+
<CardHeader>
|
|
265
|
+
<CardTitle className="flex items-center gap-2">
|
|
266
|
+
<Plus aria-hidden className="size-4" />
|
|
267
|
+
Add contact
|
|
268
|
+
</CardTitle>
|
|
269
|
+
<CardDescription>Create a development record.</CardDescription>
|
|
270
|
+
</CardHeader>
|
|
271
|
+
<CardContent>
|
|
272
|
+
<form onSubmit={(event) => void createContact(event)}>
|
|
273
|
+
<FieldGroup>
|
|
274
|
+
<Field>
|
|
275
|
+
<FieldLabel htmlFor="contact-name">Name</FieldLabel>
|
|
276
|
+
<Input
|
|
277
|
+
id="contact-name"
|
|
278
|
+
required
|
|
279
|
+
autoComplete="name"
|
|
280
|
+
value={draft.name}
|
|
281
|
+
onChange={(event) => setDraft({ ...draft, name: event.target.value })}
|
|
282
|
+
/>
|
|
283
|
+
</Field>
|
|
284
|
+
<Field>
|
|
285
|
+
<FieldLabel htmlFor="contact-email">Email</FieldLabel>
|
|
286
|
+
<Input
|
|
287
|
+
id="contact-email"
|
|
288
|
+
required
|
|
289
|
+
autoComplete="email"
|
|
290
|
+
type="email"
|
|
291
|
+
value={draft.email}
|
|
292
|
+
onChange={(event) => setDraft({ ...draft, email: event.target.value })}
|
|
293
|
+
/>
|
|
294
|
+
</Field>
|
|
295
|
+
<Field>
|
|
296
|
+
<FieldLabel htmlFor="contact-company">Company</FieldLabel>
|
|
297
|
+
<Input
|
|
298
|
+
id="contact-company"
|
|
299
|
+
autoComplete="organization"
|
|
300
|
+
value={draft.company}
|
|
301
|
+
onChange={(event) => setDraft({ ...draft, company: event.target.value })}
|
|
302
|
+
/>
|
|
303
|
+
</Field>
|
|
304
|
+
{error ? <Alert>{error}</Alert> : null}
|
|
305
|
+
<Button disabled={saving} type="submit">
|
|
306
|
+
{saving ? "Adding..." : "Add contact"}
|
|
307
|
+
</Button>
|
|
308
|
+
</FieldGroup>
|
|
309
|
+
</form>
|
|
310
|
+
</CardContent>
|
|
311
|
+
</Card>
|
|
312
|
+
</aside>
|
|
313
|
+
</main>
|
|
290
314
|
</div>
|
|
291
315
|
);
|
|
292
316
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { cn } from "@/lib/utils";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
export function Alert({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
5
|
+
return (
|
|
6
|
+
<div
|
|
7
|
+
role="alert"
|
|
8
|
+
className={cn("rounded-md border border-destructive/25 bg-destructive/10 px-3 py-2 text-sm text-destructive", className)}
|
|
9
|
+
{...props}
|
|
10
|
+
/>
|
|
11
|
+
);
|
|
12
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { cn } from "@/lib/utils";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
const badgeVariants = cva(
|
|
6
|
+
"inline-flex items-center rounded-sm border px-1.5 py-0.5 text-xs font-medium",
|
|
7
|
+
{
|
|
8
|
+
variants: {
|
|
9
|
+
variant: {
|
|
10
|
+
default: "border-transparent bg-primary text-primary-foreground",
|
|
11
|
+
secondary: "border-transparent bg-secondary text-secondary-foreground",
|
|
12
|
+
outline: "text-foreground",
|
|
13
|
+
success: "border-primary/20 bg-accent text-accent-foreground",
|
|
14
|
+
destructive: "border-destructive/20 bg-destructive/10 text-destructive",
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
defaultVariants: { variant: "secondary" },
|
|
18
|
+
},
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
export function Badge({
|
|
22
|
+
className,
|
|
23
|
+
variant,
|
|
24
|
+
...props
|
|
25
|
+
}: React.HTMLAttributes<HTMLSpanElement> & VariantProps<typeof badgeVariants>) {
|
|
26
|
+
return <span className={cn(badgeVariants({ variant }), className)} {...props} />;
|
|
27
|
+
}
|
|
@@ -4,18 +4,20 @@ import { cva, type VariantProps } from "class-variance-authority";
|
|
|
4
4
|
import * as React from "react";
|
|
5
5
|
|
|
6
6
|
const buttonVariants = cva(
|
|
7
|
-
"inline-flex
|
|
7
|
+
"inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-foreground text-sm font-medium transition-colors outline-none focus-visible:ring-2 focus-visible:ring-ring/60 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4",
|
|
8
8
|
{
|
|
9
9
|
variants: {
|
|
10
10
|
variant: {
|
|
11
|
-
default: "bg-
|
|
12
|
-
outline: "border bg-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
12
|
+
outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
|
13
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
14
|
+
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
15
|
+
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
15
16
|
},
|
|
16
17
|
size: {
|
|
17
|
-
default: "h-
|
|
18
|
-
sm: "h-
|
|
18
|
+
default: "h-9 px-3.5",
|
|
19
|
+
sm: "h-8 px-3 text-xs",
|
|
20
|
+
"icon-sm": "size-8",
|
|
19
21
|
},
|
|
20
22
|
},
|
|
21
23
|
defaultVariants: {
|
|
@@ -4,16 +4,28 @@ import * as React from "react";
|
|
|
4
4
|
export function Card({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
5
5
|
return (
|
|
6
6
|
<section
|
|
7
|
-
className={cn("rounded-
|
|
7
|
+
className={cn("rounded-lg border bg-card text-card-foreground", className)}
|
|
8
8
|
{...props}
|
|
9
9
|
/>
|
|
10
10
|
);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export function CardHeader({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
14
|
-
return <div className={cn("border-b px-
|
|
14
|
+
return <div className={cn("flex flex-col gap-1 border-b px-4 py-3", className)} {...props} />;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export function CardContent({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
18
|
-
return <div className={cn("p-
|
|
18
|
+
return <div className={cn("p-4", className)} {...props} />;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function CardTitle({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) {
|
|
22
|
+
return <h2 className={cn("m-0 text-base font-semibold", className)} {...props} />;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function CardDescription({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) {
|
|
26
|
+
return <p className={cn("m-0 text-sm text-muted-foreground", className)} {...props} />;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function CardFooter({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
30
|
+
return <div className={cn("flex items-center border-t px-4 py-3", className)} {...props} />;
|
|
19
31
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { cn } from "@/lib/utils";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
export function Empty({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
5
|
+
return <div className={cn("flex min-h-44 flex-col items-center justify-center gap-3 p-6 text-center", className)} {...props} />;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function EmptyHeader({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
9
|
+
return <div className={cn("flex max-w-sm flex-col gap-1", className)} {...props} />;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function EmptyTitle({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) {
|
|
13
|
+
return <h3 className={cn("m-0 text-sm font-semibold", className)} {...props} />;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function EmptyDescription({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) {
|
|
17
|
+
return <p className={cn("m-0 text-sm text-muted-foreground", className)} {...props} />;
|
|
18
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Label } from "@/components/ui/label";
|
|
2
|
+
import { cn } from "@/lib/utils";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
|
|
5
|
+
export function FieldGroup({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
6
|
+
return <div className={cn("flex flex-col gap-4", className)} {...props} />;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function Field({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
10
|
+
return <div className={cn("flex flex-col gap-1.5", className)} {...props} />;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function FieldLabel({ className, ...props }: React.ComponentProps<typeof Label>) {
|
|
14
|
+
return <Label className={className} {...props} />;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function FieldDescription({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) {
|
|
18
|
+
return <p className={cn("m-0 text-xs text-muted-foreground", className)} {...props} />;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function FieldError({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) {
|
|
22
|
+
return <p className={cn("m-0 text-xs text-destructive", className)} {...props} />;
|
|
23
|
+
}
|
|
@@ -7,7 +7,7 @@ export const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"in
|
|
|
7
7
|
ref={ref}
|
|
8
8
|
type={type}
|
|
9
9
|
className={cn(
|
|
10
|
-
"flex h-
|
|
10
|
+
"flex h-9 w-full rounded-md border border-input bg-background px-3 py-1 text-sm outline-none transition-shadow placeholder:text-muted-foreground focus-visible:ring-2 focus-visible:ring-ring/60 disabled:cursor-not-allowed disabled:opacity-50",
|
|
11
11
|
className,
|
|
12
12
|
)}
|
|
13
13
|
{...props}
|
|
@@ -2,5 +2,5 @@ import { cn } from "@/lib/utils";
|
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
|
|
4
4
|
export function Label({ className, ...props }: React.LabelHTMLAttributes<HTMLLabelElement>) {
|
|
5
|
-
return <label className={cn("text-sm font-medium", className)} {...props} />;
|
|
5
|
+
return <label className={cn("text-sm font-medium leading-none", className)} {...props} />;
|
|
6
6
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { cn } from "@/lib/utils";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
export function Table({ className, ...props }: React.TableHTMLAttributes<HTMLTableElement>) {
|
|
5
|
+
return (
|
|
6
|
+
<div className="w-full overflow-x-auto">
|
|
7
|
+
<table className={cn("w-full caption-bottom text-sm", className)} {...props} />
|
|
8
|
+
</div>
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function TableHeader({ className, ...props }: React.HTMLAttributes<HTMLTableSectionElement>) {
|
|
13
|
+
return <thead className={cn("bg-muted/60 [&_tr]:border-b", className)} {...props} />;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function TableBody({ className, ...props }: React.HTMLAttributes<HTMLTableSectionElement>) {
|
|
17
|
+
return <tbody className={cn("[&_tr:last-child]:border-0", className)} {...props} />;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function TableRow({ className, ...props }: React.HTMLAttributes<HTMLTableRowElement>) {
|
|
21
|
+
return <tr className={cn("border-b transition-colors hover:bg-muted/40", className)} {...props} />;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function TableHead({ className, ...props }: React.ThHTMLAttributes<HTMLTableCellElement>) {
|
|
25
|
+
return <th className={cn("h-9 px-4 text-left align-middle text-xs font-medium text-muted-foreground", className)} {...props} />;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function TableCell({ className, ...props }: React.TdHTMLAttributes<HTMLTableCellElement>) {
|
|
29
|
+
return <td className={cn("px-4 py-3 align-middle", className)} {...props} />;
|
|
30
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema.json",
|
|
3
|
+
"style": "new-york",
|
|
4
|
+
"rsc": true,
|
|
5
|
+
"tsx": true,
|
|
6
|
+
"tailwind": {
|
|
7
|
+
"config": "",
|
|
8
|
+
"css": "app/globals.css",
|
|
9
|
+
"baseColor": "neutral",
|
|
10
|
+
"cssVariables": true
|
|
11
|
+
},
|
|
12
|
+
"iconLibrary": "lucide",
|
|
13
|
+
"aliases": {
|
|
14
|
+
"components": "@/components",
|
|
15
|
+
"utils": "@/lib/utils",
|
|
16
|
+
"ui": "@/components/ui",
|
|
17
|
+
"lib": "@/lib",
|
|
18
|
+
"hooks": "@/hooks"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="next" />
|
|
2
2
|
/// <reference types="next/image-types/global" />
|
|
3
|
-
import "./.next/
|
|
3
|
+
import "./.next/types/routes.d.ts";
|
|
4
4
|
|
|
5
5
|
// NOTE: This file should not be edited
|
|
6
6
|
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|