@blawness/admin-kit 0.2.0
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/README.md +14 -0
- package/dist/auth/config.d.ts +3 -0
- package/dist/auth/config.d.ts.map +1 -0
- package/dist/auth/config.js +36 -0
- package/dist/auth/index.d.ts +6 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +46 -0
- package/dist/components/admin/editor.d.ts +5 -0
- package/dist/components/admin/editor.d.ts.map +1 -0
- package/dist/components/admin/editor.js +28 -0
- package/dist/components/admin/image-upload.d.ts +15 -0
- package/dist/components/admin/image-upload.d.ts.map +1 -0
- package/dist/components/admin/image-upload.js +50 -0
- package/dist/components/admin/toast-on-param.d.ts +5 -0
- package/dist/components/admin/toast-on-param.d.ts.map +1 -0
- package/dist/components/admin/toast-on-param.js +31 -0
- package/dist/components/confirm-delete.d.ts +14 -0
- package/dist/components/confirm-delete.d.ts.map +1 -0
- package/dist/components/confirm-delete.js +32 -0
- package/dist/components/ui/button.d.ts +9 -0
- package/dist/components/ui/button.d.ts.map +1 -0
- package/dist/components/ui/button.js +34 -0
- package/dist/components/ui/dialog.d.ts +18 -0
- package/dist/components/ui/dialog.d.ts.map +1 -0
- package/dist/components/ui/dialog.js +37 -0
- package/dist/components/ui/input.d.ts +4 -0
- package/dist/components/ui/input.d.ts.map +1 -0
- package/dist/components/ui/input.js +7 -0
- package/dist/components.d.ts +7 -0
- package/dist/components.d.ts.map +1 -0
- package/dist/components.js +6 -0
- package/dist/db/index.d.ts +6 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +8 -0
- package/dist/db/schema.d.ts +202 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +16 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/lib/admin/media.d.ts +16 -0
- package/dist/lib/admin/media.d.ts.map +1 -0
- package/dist/lib/admin/media.js +13 -0
- package/dist/lib/admin/users.d.ts +12 -0
- package/dist/lib/admin/users.d.ts.map +1 -0
- package/dist/lib/admin/users.js +24 -0
- package/dist/lib/auth-helpers.d.ts +5 -0
- package/dist/lib/auth-helpers.d.ts.map +1 -0
- package/dist/lib/auth-helpers.js +16 -0
- package/dist/lib/db-errors.d.ts +5 -0
- package/dist/lib/db-errors.d.ts.map +1 -0
- package/dist/lib/db-errors.js +14 -0
- package/dist/lib/r2.d.ts +24 -0
- package/dist/lib/r2.d.ts.map +1 -0
- package/dist/lib/r2.js +59 -0
- package/dist/lib/sanitize.d.ts +14 -0
- package/dist/lib/sanitize.d.ts.map +1 -0
- package/dist/lib/sanitize.js +28 -0
- package/dist/lib/slug.d.ts +2 -0
- package/dist/lib/slug.d.ts.map +1 -0
- package/dist/lib/slug.js +9 -0
- package/dist/lib/utils.d.ts +3 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +5 -0
- package/dist/screens/login/actions.d.ts +5 -0
- package/dist/screens/login/actions.d.ts.map +1 -0
- package/dist/screens/login/actions.js +28 -0
- package/dist/screens/login/page.d.ts +3 -0
- package/dist/screens/login/page.d.ts.map +1 -0
- package/dist/screens/login/page.js +11 -0
- package/dist/screens/media/actions.d.ts +5 -0
- package/dist/screens/media/actions.d.ts.map +1 -0
- package/dist/screens/media/actions.js +32 -0
- package/dist/screens/media/lib.d.ts +9 -0
- package/dist/screens/media/lib.d.ts.map +1 -0
- package/dist/screens/media/lib.js +30 -0
- package/dist/screens/media/page.d.ts +8 -0
- package/dist/screens/media/page.d.ts.map +1 -0
- package/dist/screens/media/page.js +13 -0
- package/dist/screens/media/uploader.d.ts +2 -0
- package/dist/screens/media/uploader.d.ts.map +1 -0
- package/dist/screens/media/uploader.js +10 -0
- package/dist/screens/users/actions.d.ts +5 -0
- package/dist/screens/users/actions.d.ts.map +1 -0
- package/dist/screens/users/actions.js +70 -0
- package/dist/screens/users/page.d.ts +7 -0
- package/dist/screens/users/page.d.ts.map +1 -0
- package/dist/screens/users/page.js +22 -0
- package/dist/shell/actions.d.ts +2 -0
- package/dist/shell/actions.d.ts.map +1 -0
- package/dist/shell/actions.js +5 -0
- package/dist/shell/layout.d.ts +9 -0
- package/dist/shell/layout.d.ts.map +1 -0
- package/dist/shell/layout.js +15 -0
- package/dist/shell/sidebar.d.ts +16 -0
- package/dist/shell/sidebar.d.ts.map +1 -0
- package/dist/shell/sidebar.js +19 -0
- package/package.json +148 -0
- package/src/auth/config.ts +36 -0
- package/src/auth/index.ts +49 -0
- package/src/components/admin/editor.tsx +53 -0
- package/src/components/admin/image-upload.tsx +128 -0
- package/src/components/admin/toast-on-param.tsx +47 -0
- package/src/components/confirm-delete.tsx +96 -0
- package/src/components/ui/button.tsx +58 -0
- package/src/components/ui/dialog.tsx +160 -0
- package/src/components/ui/input.tsx +20 -0
- package/src/components.ts +17 -0
- package/src/db/index.ts +8 -0
- package/src/db/schema.ts +23 -0
- package/src/index.ts +7 -0
- package/src/lib/admin/media.ts +16 -0
- package/src/lib/admin/users.ts +31 -0
- package/src/lib/auth-helpers.ts +16 -0
- package/src/lib/db-errors.ts +18 -0
- package/src/lib/r2.ts +70 -0
- package/src/lib/sanitize.ts +29 -0
- package/src/lib/slug.ts +9 -0
- package/src/lib/utils.ts +6 -0
- package/src/screens/login/actions.ts +38 -0
- package/src/screens/login/page.tsx +48 -0
- package/src/screens/media/actions.ts +34 -0
- package/src/screens/media/lib.ts +39 -0
- package/src/screens/media/page.tsx +82 -0
- package/src/screens/media/uploader.tsx +19 -0
- package/src/screens/users/actions.ts +71 -0
- package/src/screens/users/page.tsx +128 -0
- package/src/shell/actions.ts +6 -0
- package/src/shell/layout.tsx +47 -0
- package/src/shell/sidebar.tsx +74 -0
- package/src/types/next-auth.d.ts +20 -0
package/README.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# @blawness/admin-kit
|
|
2
|
+
|
|
3
|
+
Reusable CMS core (auth, media, users, editor, admin shell, shared components)
|
|
4
|
+
extracted from the LIPAN RI site. Consumed as a private Git dependency.
|
|
5
|
+
|
|
6
|
+
## Consumer setup
|
|
7
|
+
1. `pnpm add github:Blawness/admin-kit#vX.Y.Z`
|
|
8
|
+
2. Add to `next.config`: `transpilePackages: ["@blawness/admin-kit"]`
|
|
9
|
+
3. Ensure Tailwind scans the package and defines the `navy`/`brand`/`gold` tokens.
|
|
10
|
+
|
|
11
|
+
## Phase 1 result
|
|
12
|
+
- Directive preservation: CONFIRMED (`"use client"` intact in dist after tsc build).
|
|
13
|
+
- Consumed by a Next.js 16.2.7 throwaway app via `transpilePackages` + Git-tag dependency: build PASSED, page rendered (HTTP 200).
|
|
14
|
+
- Gotchas: pnpm v10 blocks the git dep's `prepare` script by default (`ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED`) — the consumer must add `@blawness/admin-kit` to `pnpm.onlyBuiltDependencies` in package.json (or pnpm-workspace.yaml) so tsc runs and `dist/` builds. The `noop` server action can be typed `() => Promise<void>` even though `ConfirmDelete`'s `action` prop is `(formData: FormData) => Promise<void>` — Next/TS accepts the narrower no-arg signature.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/auth/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEhD,eAAO,MAAM,UAAU,EAAE,cAiCxB,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export const authConfig = {
|
|
2
|
+
pages: { signIn: "/admin/login" },
|
|
3
|
+
session: { strategy: "jwt" },
|
|
4
|
+
trustHost: true,
|
|
5
|
+
providers: [], // real provider added in auth/index.ts (Node runtime)
|
|
6
|
+
callbacks: {
|
|
7
|
+
authorized({ auth, request: { nextUrl } }) {
|
|
8
|
+
const isAdminArea = nextUrl.pathname.startsWith("/admin");
|
|
9
|
+
const isLogin = nextUrl.pathname === "/admin/login";
|
|
10
|
+
// Already-authenticated users have no business on the login page.
|
|
11
|
+
if (isLogin) {
|
|
12
|
+
return auth?.user
|
|
13
|
+
? Response.redirect(new URL("/admin", nextUrl))
|
|
14
|
+
: true;
|
|
15
|
+
}
|
|
16
|
+
if (isAdminArea)
|
|
17
|
+
return !!auth?.user;
|
|
18
|
+
return true;
|
|
19
|
+
},
|
|
20
|
+
jwt({ token, user }) {
|
|
21
|
+
if (user) {
|
|
22
|
+
token.id = user.id;
|
|
23
|
+
token.role = user.role ?? "editor";
|
|
24
|
+
}
|
|
25
|
+
return token;
|
|
26
|
+
},
|
|
27
|
+
session({ session, token }) {
|
|
28
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
29
|
+
const u = session.user;
|
|
30
|
+
if (token.id)
|
|
31
|
+
u.id = token.id;
|
|
32
|
+
u.role = token.role ?? "editor";
|
|
33
|
+
return session;
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type NextAuthResult } from "next-auth";
|
|
2
|
+
export declare const handlers: NextAuthResult["handlers"];
|
|
3
|
+
export declare const auth: NextAuthResult["auth"];
|
|
4
|
+
export declare const signIn: NextAuthResult["signIn"];
|
|
5
|
+
export declare const signOut: NextAuthResult["signOut"];
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,OAAiB,EAAE,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AA6C1D,eAAO,MAAM,QAAQ,EAAE,cAAc,CAAC,UAAU,CAAoB,CAAC;AACrE,eAAO,MAAM,IAAI,EAAE,cAAc,CAAC,MAAM,CAAgB,CAAC;AACzD,eAAO,MAAM,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAkB,CAAC;AAC/D,eAAO,MAAM,OAAO,EAAE,cAAc,CAAC,SAAS,CAAmB,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import NextAuth from "next-auth";
|
|
2
|
+
import Credentials from "next-auth/providers/credentials";
|
|
3
|
+
import { compare } from "bcryptjs";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { eq } from "drizzle-orm";
|
|
6
|
+
import { db } from "../db/index";
|
|
7
|
+
import { users } from "../db/schema";
|
|
8
|
+
import { authConfig } from "./config";
|
|
9
|
+
const credentialsSchema = z.object({
|
|
10
|
+
email: z.string().email(),
|
|
11
|
+
password: z.string().min(1),
|
|
12
|
+
});
|
|
13
|
+
const _result = NextAuth({
|
|
14
|
+
...authConfig,
|
|
15
|
+
providers: [
|
|
16
|
+
Credentials({
|
|
17
|
+
credentials: { email: {}, password: {} },
|
|
18
|
+
async authorize(raw) {
|
|
19
|
+
const parsed = credentialsSchema.safeParse(raw);
|
|
20
|
+
if (!parsed.success)
|
|
21
|
+
return null;
|
|
22
|
+
const { email, password } = parsed.data;
|
|
23
|
+
const [user] = await db
|
|
24
|
+
.select()
|
|
25
|
+
.from(users)
|
|
26
|
+
.where(eq(users.email, email))
|
|
27
|
+
.limit(1);
|
|
28
|
+
if (!user?.passwordHash)
|
|
29
|
+
return null;
|
|
30
|
+
const ok = await compare(password, user.passwordHash);
|
|
31
|
+
if (!ok)
|
|
32
|
+
return null;
|
|
33
|
+
return {
|
|
34
|
+
id: String(user.id),
|
|
35
|
+
email: user.email,
|
|
36
|
+
name: user.name,
|
|
37
|
+
role: user.role ?? "editor",
|
|
38
|
+
};
|
|
39
|
+
},
|
|
40
|
+
}),
|
|
41
|
+
],
|
|
42
|
+
});
|
|
43
|
+
export const handlers = _result.handlers;
|
|
44
|
+
export const auth = _result.auth;
|
|
45
|
+
export const signIn = _result.signIn;
|
|
46
|
+
export const signOut = _result.signOut;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../../../src/components/admin/editor.tsx"],"names":[],"mappings":"AAQA,wBAAgB,MAAM,CAAC,EACrB,KAAK,EACL,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAClC,sCA8BA"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEditor, EditorContent } from "@tiptap/react";
|
|
4
|
+
import StarterKit from "@tiptap/starter-kit";
|
|
5
|
+
import Link from "@tiptap/extension-link";
|
|
6
|
+
import Image from "@tiptap/extension-image";
|
|
7
|
+
import { Button } from "../ui/button";
|
|
8
|
+
export function Editor({ value, onChange, }) {
|
|
9
|
+
const editor = useEditor({
|
|
10
|
+
extensions: [
|
|
11
|
+
StarterKit.configure({ heading: { levels: [2, 3, 4] } }),
|
|
12
|
+
Link.configure({ openOnClick: false }),
|
|
13
|
+
Image,
|
|
14
|
+
],
|
|
15
|
+
content: value,
|
|
16
|
+
immediatelyRender: false,
|
|
17
|
+
editorProps: { attributes: { class: "prose prose-blue max-w-none min-h-[300px] focus:outline-none" } },
|
|
18
|
+
onUpdate: ({ editor }) => onChange(editor.getHTML()),
|
|
19
|
+
});
|
|
20
|
+
if (!editor)
|
|
21
|
+
return null;
|
|
22
|
+
return (_jsxs("div", { className: "rounded-md border border-navy-200", children: [_jsxs("div", { className: "flex flex-wrap gap-1 border-b border-navy-100 p-2", children: [_jsx(ToolbarButton, { on: () => editor.chain().focus().toggleBold().run(), active: editor.isActive("bold"), children: "B" }), _jsx(ToolbarButton, { on: () => editor.chain().focus().toggleItalic().run(), active: editor.isActive("italic"), children: "I" }), _jsx(ToolbarButton, { on: () => editor.chain().focus().toggleHeading({ level: 2 }).run(), active: editor.isActive("heading", { level: 2 }), children: "H2" }), _jsx(ToolbarButton, { on: () => editor.chain().focus().toggleHeading({ level: 3 }).run(), active: editor.isActive("heading", { level: 3 }), children: "H3" }), _jsx(ToolbarButton, { on: () => editor.chain().focus().toggleBulletList().run(), active: editor.isActive("bulletList"), children: "\u2022 List" }), _jsx(ToolbarButton, { on: () => editor.chain().focus().toggleOrderedList().run(), active: editor.isActive("orderedList"), children: "1. List" }), _jsx(ToolbarButton, { on: () => { const url = prompt("URL tautan:"); if (url)
|
|
23
|
+
editor.chain().focus().setLink({ href: url }).run(); }, active: editor.isActive("link"), children: "Link" }), _jsx(ToolbarButton, { on: () => { const url = prompt("URL gambar:"); if (url)
|
|
24
|
+
editor.chain().focus().setImage({ src: url }).run(); }, active: false, children: "Gambar" })] }), _jsx(EditorContent, { editor: editor, className: "p-3" })] }));
|
|
25
|
+
}
|
|
26
|
+
function ToolbarButton({ on, active, children }) {
|
|
27
|
+
return (_jsx(Button, { type: "button", variant: active ? "default" : "outline", size: "sm", onClick: on, children: children }));
|
|
28
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clear drag-and-drop / click-to-upload zone backed by the R2 upload action.
|
|
3
|
+
* Used for both a post's featured image (with `value`) and the gallery (no
|
|
4
|
+
* value, parent refreshes on change).
|
|
5
|
+
*/
|
|
6
|
+
export declare function ImageUpload({ value, onChange, label, uploadAction, }: {
|
|
7
|
+
value?: string | null;
|
|
8
|
+
onChange: (url: string) => void;
|
|
9
|
+
label?: string;
|
|
10
|
+
uploadAction: (formData: FormData) => Promise<{
|
|
11
|
+
url?: string;
|
|
12
|
+
error?: string;
|
|
13
|
+
}>;
|
|
14
|
+
}): import("react").JSX.Element;
|
|
15
|
+
//# sourceMappingURL=image-upload.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-upload.d.ts","sourceRoot":"","sources":["../../../src/components/admin/image-upload.tsx"],"names":[],"mappings":"AASA;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,QAAQ,EACR,KAAgB,EAChB,YAAY,GACb,EAAE;IACD,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,OAAO,CAAC;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACjF,+BAuGA"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useRef, useState, useTransition } from "react";
|
|
4
|
+
import { UploadCloud, Loader2, AlertCircle } from "lucide-react";
|
|
5
|
+
// Mirror the server action (src/app/admin/media/actions.ts) — keep in sync.
|
|
6
|
+
const OK_TYPES = ["image/jpeg", "image/png", "image/webp", "image/gif"];
|
|
7
|
+
const MAX_BYTES = 8 * 1024 * 1024;
|
|
8
|
+
/**
|
|
9
|
+
* Clear drag-and-drop / click-to-upload zone backed by the R2 upload action.
|
|
10
|
+
* Used for both a post's featured image (with `value`) and the gallery (no
|
|
11
|
+
* value, parent refreshes on change).
|
|
12
|
+
*/
|
|
13
|
+
export function ImageUpload({ value, onChange, label = "gambar", uploadAction, }) {
|
|
14
|
+
const [pending, start] = useTransition();
|
|
15
|
+
const [error, setError] = useState();
|
|
16
|
+
const [dragging, setDragging] = useState(false);
|
|
17
|
+
const inputRef = useRef(null);
|
|
18
|
+
function upload(file) {
|
|
19
|
+
if (!file)
|
|
20
|
+
return;
|
|
21
|
+
if (!OK_TYPES.includes(file.type)) {
|
|
22
|
+
setError("Format tidak didukung — gunakan JPG, PNG, WebP, atau GIF.");
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (file.size > MAX_BYTES) {
|
|
26
|
+
setError("Ukuran gambar maksimal 8MB.");
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const fd = new FormData();
|
|
30
|
+
fd.set("file", file);
|
|
31
|
+
setError(undefined);
|
|
32
|
+
start(async () => {
|
|
33
|
+
const res = await uploadAction(fd);
|
|
34
|
+
if (res.error)
|
|
35
|
+
setError(res.error);
|
|
36
|
+
else if (res.url)
|
|
37
|
+
onChange(res.url);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
return (_jsxs("div", { className: "space-y-2", children: [_jsx("button", { type: "button", onClick: () => inputRef.current?.click(), onDragOver: (e) => {
|
|
41
|
+
e.preventDefault();
|
|
42
|
+
setDragging(true);
|
|
43
|
+
}, onDragLeave: () => setDragging(false), onDrop: (e) => {
|
|
44
|
+
e.preventDefault();
|
|
45
|
+
setDragging(false);
|
|
46
|
+
upload(e.dataTransfer.files?.[0]);
|
|
47
|
+
}, disabled: pending, className: `group relative flex w-full flex-col items-center justify-center gap-3 rounded-xl border-2 border-dashed px-6 py-8 text-center transition-colors ${dragging
|
|
48
|
+
? "border-brand-500 bg-brand-50"
|
|
49
|
+
: "border-navy-200 bg-navy-50/40 hover:border-brand-400 hover:bg-brand-50/60"} ${pending ? "pointer-events-none opacity-80" : "cursor-pointer"}`, children: value ? (_jsxs(_Fragment, { children: [_jsx("img", { src: value, alt: "", className: "h-28 w-28 rounded-lg object-cover object-top ring-1 ring-navy-200 shadow-sm" }), _jsxs("span", { className: "text-sm font-medium text-navy-700", children: ["Klik untuk mengganti ", label] }), pending && (_jsx("span", { className: "absolute inset-0 grid place-items-center rounded-xl bg-white/70", children: _jsx(Loader2, { className: "h-6 w-6 animate-spin text-brand-600" }) }))] })) : (_jsxs(_Fragment, { children: [_jsx("span", { className: "flex h-12 w-12 items-center justify-center rounded-full bg-brand-100 text-brand-600 transition-transform group-hover:scale-105", children: pending ? (_jsx(Loader2, { className: "h-6 w-6 animate-spin" })) : (_jsx(UploadCloud, { className: "h-6 w-6" })) }), _jsxs("span", { children: [_jsx("span", { className: "block text-sm font-semibold text-navy-900", children: pending ? "Mengunggah…" : `Klik untuk unggah ${label}` }), _jsx("span", { className: "mt-0.5 block text-xs text-muted-foreground", children: "atau seret & lepas di sini \u00B7 JPG, PNG, WebP, GIF (maks 8MB)" })] })] })) }), _jsx("input", { ref: inputRef, type: "file", accept: "image/*", className: "hidden", onChange: (e) => upload(e.target.files?.[0]), disabled: pending }), error && (_jsxs("p", { className: "flex items-center gap-1.5 text-sm text-red-600", children: [_jsx(AlertCircle, { className: "h-4 w-4 shrink-0" }), error] }))] }));
|
|
50
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toast-on-param.d.ts","sourceRoot":"","sources":["../../../src/components/admin/toast-on-param.tsx"],"names":[],"mappings":"AAqCA,wBAAgB,YAAY,CAAC,KAAK,EAAE;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC,+BAMA"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { Suspense, useEffect, useRef } from "react";
|
|
4
|
+
import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
|
5
|
+
import { toast } from "sonner";
|
|
6
|
+
function ToastOnParamInner({ param, messages, }) {
|
|
7
|
+
const searchParams = useSearchParams();
|
|
8
|
+
const router = useRouter();
|
|
9
|
+
const pathname = usePathname();
|
|
10
|
+
const fired = useRef(false);
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
if (fired.current)
|
|
13
|
+
return;
|
|
14
|
+
const value = searchParams.get(param);
|
|
15
|
+
if (!value)
|
|
16
|
+
return;
|
|
17
|
+
fired.current = true;
|
|
18
|
+
const message = messages[value];
|
|
19
|
+
if (message)
|
|
20
|
+
toast.success(message);
|
|
21
|
+
// Bersihkan param dari URL agar tidak terpicu ulang saat refresh.
|
|
22
|
+
const next = new URLSearchParams(searchParams.toString());
|
|
23
|
+
next.delete(param);
|
|
24
|
+
const qs = next.toString();
|
|
25
|
+
router.replace(qs ? `${pathname}?${qs}` : pathname);
|
|
26
|
+
}, [param, messages, searchParams, router, pathname]);
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
export function ToastOnParam(props) {
|
|
30
|
+
return (_jsx(Suspense, { fallback: null, children: _jsx(ToastOnParamInner, { ...props }) }));
|
|
31
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type ReactElement, type ReactNode } from "react";
|
|
2
|
+
export declare function ConfirmDelete({ action, id, title, description, confirmLabel, successMessage, trigger, }: {
|
|
3
|
+
/** Server action that receives FormData with an `id` field. */
|
|
4
|
+
action: (formData: FormData) => Promise<void>;
|
|
5
|
+
id: number;
|
|
6
|
+
title?: string;
|
|
7
|
+
description?: ReactNode;
|
|
8
|
+
confirmLabel?: string;
|
|
9
|
+
/** Toast yang muncul setelah penghapusan berhasil. */
|
|
10
|
+
successMessage?: string;
|
|
11
|
+
/** Custom trigger element. Defaults to an outline icon button. */
|
|
12
|
+
trigger?: ReactElement;
|
|
13
|
+
}): import("react").JSX.Element;
|
|
14
|
+
//# sourceMappingURL=confirm-delete.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confirm-delete.d.ts","sourceRoot":"","sources":["../../src/components/confirm-delete.tsx"],"names":[],"mappings":"AAEA,OAAO,EAA2B,KAAK,YAAY,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAenF,wBAAgB,aAAa,CAAC,EAC5B,MAAM,EACN,EAAE,EACF,KAAyB,EACzB,WAAoD,EACpD,YAAsB,EACtB,cAAoC,EACpC,OAAO,GACR,EAAE;IACD,+DAA+D;IAC/D,MAAM,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sDAAsD;IACtD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kEAAkE;IAClE,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB,+BA2DA"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useTransition } from "react";
|
|
4
|
+
import { Loader2, Trash2 } from "lucide-react";
|
|
5
|
+
import { toast } from "sonner";
|
|
6
|
+
import { Button } from "./ui/button";
|
|
7
|
+
import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "./ui/dialog";
|
|
8
|
+
export function ConfirmDelete({ action, id, title = "Hapus item ini?", description = "Tindakan ini tidak dapat dibatalkan.", confirmLabel = "Hapus", successMessage = "Berhasil dihapus.", trigger, }) {
|
|
9
|
+
const [open, setOpen] = useState(false);
|
|
10
|
+
const [pending, start] = useTransition();
|
|
11
|
+
function handleConfirm() {
|
|
12
|
+
start(async () => {
|
|
13
|
+
const fd = new FormData();
|
|
14
|
+
fd.set("id", String(id));
|
|
15
|
+
let succeeded = false;
|
|
16
|
+
try {
|
|
17
|
+
await action(fd);
|
|
18
|
+
// Hanya tercapai bila action selesai tanpa melempar (mis. redirect
|
|
19
|
+
// pada konflik akan throw, sehingga toast sukses tidak muncul).
|
|
20
|
+
succeeded = true;
|
|
21
|
+
}
|
|
22
|
+
finally {
|
|
23
|
+
// Tutup dialog baik saat sukses maupun saat action me-redirect
|
|
24
|
+
// (mis. media yang masih dipakai → halaman menampilkan pesan error).
|
|
25
|
+
setOpen(false);
|
|
26
|
+
}
|
|
27
|
+
if (succeeded)
|
|
28
|
+
toast.success(successMessage);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
return (_jsxs(Dialog, { open: open, onOpenChange: setOpen, children: [_jsx(DialogTrigger, { render: trigger ?? (_jsx(Button, { size: "sm", variant: "outline", "aria-label": "Hapus", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) })) }), _jsxs(DialogContent, { showCloseButton: false, children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: title }), _jsx(DialogDescription, { children: description })] }), _jsxs(DialogFooter, { children: [_jsx(DialogClose, { render: _jsx(Button, { variant: "outline", disabled: pending }), children: "Batal" }), _jsxs(Button, { variant: "destructive", onClick: handleConfirm, disabled: pending, children: [pending ? (_jsx(Loader2, { className: "h-4 w-4 animate-spin" })) : (_jsx(Trash2, { className: "h-4 w-4" })), confirmLabel] })] })] })] }));
|
|
32
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Button as ButtonPrimitive } from "@base-ui/react/button";
|
|
2
|
+
import { type VariantProps } from "class-variance-authority";
|
|
3
|
+
declare const buttonVariants: (props?: ({
|
|
4
|
+
variant?: "default" | "outline" | "secondary" | "ghost" | "destructive" | "link" | null | undefined;
|
|
5
|
+
size?: "default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | null | undefined;
|
|
6
|
+
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
7
|
+
declare function Button({ className, variant, size, ...props }: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>): import("react").JSX.Element;
|
|
8
|
+
export { Button, buttonVariants };
|
|
9
|
+
//# sourceMappingURL=button.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"button.d.ts","sourceRoot":"","sources":["../../../src/components/ui/button.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACjE,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAIjE,QAAA,MAAM,cAAc;;;8EAmCnB,CAAA;AAED,iBAAS,MAAM,CAAC,EACd,SAAS,EACT,OAAmB,EACnB,IAAgB,EAChB,GAAG,KAAK,EACT,EAAE,eAAe,CAAC,KAAK,GAAG,YAAY,CAAC,OAAO,cAAc,CAAC,+BAQ7D;AAED,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,CAAA"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Button as ButtonPrimitive } from "@base-ui/react/button";
|
|
3
|
+
import { cva } from "class-variance-authority";
|
|
4
|
+
import { cn } from "../../lib/utils";
|
|
5
|
+
const buttonVariants = cva("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", {
|
|
6
|
+
variants: {
|
|
7
|
+
variant: {
|
|
8
|
+
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
|
|
9
|
+
outline: "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",
|
|
10
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
|
|
11
|
+
ghost: "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
|
|
12
|
+
destructive: "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",
|
|
13
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
14
|
+
},
|
|
15
|
+
size: {
|
|
16
|
+
default: "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
|
|
17
|
+
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",
|
|
18
|
+
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",
|
|
19
|
+
lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
|
|
20
|
+
icon: "size-8",
|
|
21
|
+
"icon-xs": "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
|
|
22
|
+
"icon-sm": "size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
|
|
23
|
+
"icon-lg": "size-9",
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
defaultVariants: {
|
|
27
|
+
variant: "default",
|
|
28
|
+
size: "default",
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
function Button({ className, variant = "default", size = "default", ...props }) {
|
|
32
|
+
return (_jsx(ButtonPrimitive, { "data-slot": "button", className: cn(buttonVariants({ variant, size, className })), ...props }));
|
|
33
|
+
}
|
|
34
|
+
export { Button, buttonVariants };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Dialog as DialogPrimitive } from "@base-ui/react/dialog";
|
|
3
|
+
declare function Dialog({ ...props }: DialogPrimitive.Root.Props): React.JSX.Element;
|
|
4
|
+
declare function DialogTrigger({ ...props }: DialogPrimitive.Trigger.Props): React.JSX.Element;
|
|
5
|
+
declare function DialogPortal({ ...props }: DialogPrimitive.Portal.Props): React.JSX.Element;
|
|
6
|
+
declare function DialogClose({ ...props }: DialogPrimitive.Close.Props): React.JSX.Element;
|
|
7
|
+
declare function DialogOverlay({ className, ...props }: DialogPrimitive.Backdrop.Props): React.JSX.Element;
|
|
8
|
+
declare function DialogContent({ className, children, showCloseButton, ...props }: DialogPrimitive.Popup.Props & {
|
|
9
|
+
showCloseButton?: boolean;
|
|
10
|
+
}): React.JSX.Element;
|
|
11
|
+
declare function DialogHeader({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
|
|
12
|
+
declare function DialogFooter({ className, showCloseButton, children, ...props }: React.ComponentProps<"div"> & {
|
|
13
|
+
showCloseButton?: boolean;
|
|
14
|
+
}): React.JSX.Element;
|
|
15
|
+
declare function DialogTitle({ className, ...props }: DialogPrimitive.Title.Props): React.JSX.Element;
|
|
16
|
+
declare function DialogDescription({ className, ...props }: DialogPrimitive.Description.Props): React.JSX.Element;
|
|
17
|
+
export { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, };
|
|
18
|
+
//# sourceMappingURL=dialog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dialog.d.ts","sourceRoot":"","sources":["../../../src/components/ui/dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,MAAM,IAAI,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAMjE,iBAAS,MAAM,CAAC,EAAE,GAAG,KAAK,EAAE,EAAE,eAAe,CAAC,IAAI,CAAC,KAAK,qBAEvD;AAED,iBAAS,aAAa,CAAC,EAAE,GAAG,KAAK,EAAE,EAAE,eAAe,CAAC,OAAO,CAAC,KAAK,qBAEjE;AAED,iBAAS,YAAY,CAAC,EAAE,GAAG,KAAK,EAAE,EAAE,eAAe,CAAC,MAAM,CAAC,KAAK,qBAE/D;AAED,iBAAS,WAAW,CAAC,EAAE,GAAG,KAAK,EAAE,EAAE,eAAe,CAAC,KAAK,CAAC,KAAK,qBAE7D;AAED,iBAAS,aAAa,CAAC,EACrB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,eAAe,CAAC,QAAQ,CAAC,KAAK,qBAWhC;AAED,iBAAS,aAAa,CAAC,EACrB,SAAS,EACT,QAAQ,EACR,eAAsB,EACtB,GAAG,KAAK,EACT,EAAE,eAAe,CAAC,KAAK,CAAC,KAAK,GAAG;IAC/B,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B,qBAgCA;AAED,iBAAS,YAAY,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAQzE;AAED,iBAAS,YAAY,CAAC,EACpB,SAAS,EACT,eAAuB,EACvB,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG;IAC/B,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B,qBAkBA;AAED,iBAAS,WAAW,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,eAAe,CAAC,KAAK,CAAC,KAAK,qBAWxE;AAED,iBAAS,iBAAiB,CAAC,EACzB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,eAAe,CAAC,WAAW,CAAC,KAAK,qBAWnC;AAED,OAAO,EACL,MAAM,EACN,WAAW,EACX,aAAa,EACb,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,WAAW,EACX,aAAa,GACd,CAAA"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Dialog as DialogPrimitive } from "@base-ui/react/dialog";
|
|
4
|
+
import { cn } from "../../lib/utils";
|
|
5
|
+
import { Button } from "./button";
|
|
6
|
+
import { XIcon } from "lucide-react";
|
|
7
|
+
function Dialog({ ...props }) {
|
|
8
|
+
return _jsx(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
|
|
9
|
+
}
|
|
10
|
+
function DialogTrigger({ ...props }) {
|
|
11
|
+
return _jsx(DialogPrimitive.Trigger, { "data-slot": "dialog-trigger", ...props });
|
|
12
|
+
}
|
|
13
|
+
function DialogPortal({ ...props }) {
|
|
14
|
+
return _jsx(DialogPrimitive.Portal, { "data-slot": "dialog-portal", ...props });
|
|
15
|
+
}
|
|
16
|
+
function DialogClose({ ...props }) {
|
|
17
|
+
return _jsx(DialogPrimitive.Close, { "data-slot": "dialog-close", ...props });
|
|
18
|
+
}
|
|
19
|
+
function DialogOverlay({ className, ...props }) {
|
|
20
|
+
return (_jsx(DialogPrimitive.Backdrop, { "data-slot": "dialog-overlay", className: cn("fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0", className), ...props }));
|
|
21
|
+
}
|
|
22
|
+
function DialogContent({ className, children, showCloseButton = true, ...props }) {
|
|
23
|
+
return (_jsxs(DialogPortal, { children: [_jsx(DialogOverlay, {}), _jsxs(DialogPrimitive.Popup, { "data-slot": "dialog-content", className: cn("fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-sm text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className), ...props, children: [children, showCloseButton && (_jsxs(DialogPrimitive.Close, { "data-slot": "dialog-close", render: _jsx(Button, { variant: "ghost", className: "absolute top-2 right-2", size: "icon-sm" }), children: [_jsx(XIcon, {}), _jsx("span", { className: "sr-only", children: "Close" })] }))] })] }));
|
|
24
|
+
}
|
|
25
|
+
function DialogHeader({ className, ...props }) {
|
|
26
|
+
return (_jsx("div", { "data-slot": "dialog-header", className: cn("flex flex-col gap-2", className), ...props }));
|
|
27
|
+
}
|
|
28
|
+
function DialogFooter({ className, showCloseButton = false, children, ...props }) {
|
|
29
|
+
return (_jsxs("div", { "data-slot": "dialog-footer", className: cn("-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 sm:flex-row sm:justify-end", className), ...props, children: [children, showCloseButton && (_jsx(DialogPrimitive.Close, { render: _jsx(Button, { variant: "outline" }), children: "Close" }))] }));
|
|
30
|
+
}
|
|
31
|
+
function DialogTitle({ className, ...props }) {
|
|
32
|
+
return (_jsx(DialogPrimitive.Title, { "data-slot": "dialog-title", className: cn("font-heading text-base leading-none font-medium", className), ...props }));
|
|
33
|
+
}
|
|
34
|
+
function DialogDescription({ className, ...props }) {
|
|
35
|
+
return (_jsx(DialogPrimitive.Description, { "data-slot": "dialog-description", className: cn("text-sm text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground", className), ...props }));
|
|
36
|
+
}
|
|
37
|
+
export { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../../src/components/ui/input.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAK9B,iBAAS,KAAK,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,CAAC,qBAY1E;AAED,OAAO,EAAE,KAAK,EAAE,CAAA"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Input as InputPrimitive } from "@base-ui/react/input";
|
|
3
|
+
import { cn } from "../../lib/utils";
|
|
4
|
+
function Input({ className, type, ...props }) {
|
|
5
|
+
return (_jsx(InputPrimitive, { type: type, "data-slot": "input", className: cn("h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40", className), ...props }));
|
|
6
|
+
}
|
|
7
|
+
export { Input };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { ConfirmDelete } from "./components/confirm-delete";
|
|
2
|
+
export { Button, buttonVariants } from "./components/ui/button";
|
|
3
|
+
export { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, } from "./components/ui/dialog";
|
|
4
|
+
export * from "./components/admin/editor";
|
|
5
|
+
export * from "./components/admin/image-upload";
|
|
6
|
+
export * from "./components/admin/toast-on-param";
|
|
7
|
+
//# sourceMappingURL=components.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"components.d.ts","sourceRoot":"","sources":["../src/components.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EACL,MAAM,EACN,WAAW,EACX,aAAa,EACb,iBAAiB,EACjB,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,WAAW,EACX,aAAa,GACd,MAAM,wBAAwB,CAAC;AAChC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iCAAiC,CAAC;AAChD,cAAc,mCAAmC,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { ConfirmDelete } from "./components/confirm-delete";
|
|
2
|
+
export { Button, buttonVariants } from "./components/ui/button";
|
|
3
|
+
export { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, } from "./components/ui/dialog";
|
|
4
|
+
export * from "./components/admin/editor";
|
|
5
|
+
export * from "./components/admin/image-upload";
|
|
6
|
+
export * from "./components/admin/toast-on-param";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"AACA,OAAO,QAAQ,MAAM,UAAU,CAAC;AAChC,OAAO,KAAK,MAAM,MAAM,UAAU,CAAC;AAKnC,eAAO,MAAM,EAAE;;CAA8B,CAAC"}
|
package/dist/db/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { drizzle } from "drizzle-orm/postgres-js";
|
|
2
|
+
import postgres from "postgres";
|
|
3
|
+
import * as schema from "./schema";
|
|
4
|
+
const connectionString = process.env.DATABASE_URL;
|
|
5
|
+
if (!connectionString)
|
|
6
|
+
throw new Error("admin-kit: DATABASE_URL env var is required");
|
|
7
|
+
const client = postgres(connectionString, { prepare: false });
|
|
8
|
+
export const db = drizzle(client, { schema });
|