diff --git a/.changeset/poor-meals-yawn.md b/.changeset/poor-meals-yawn.md new file mode 100644 index 00000000..66bcab50 --- /dev/null +++ b/.changeset/poor-meals-yawn.md @@ -0,0 +1,10 @@ +--- +"@oberoncms/adapter-vercel-postgres": minor +"@oberoncms/adapter-turso": minor +"@oberoncms/upload-thing": minor +"@oberoncms/core": minor +"@tohuhono/utils": minor +--- + +Added updatedAt and updatedBy to images +Resolved uploadingthing type declaration error diff --git a/oberoncms/adapter-turso/package.json b/oberoncms/adapter-turso/package.json index f4842059..278fd1f3 100644 --- a/oberoncms/adapter-turso/package.json +++ b/oberoncms/adapter-turso/package.json @@ -29,7 +29,7 @@ "scripts": { "build": "vite build", "clean": "node ../../scripts/clean-package.mjs", - "db:migrations:create": "drizzle-kit generate:sqlite", + "generate:migrations": "drizzle-kit generate:sqlite", "dev": "vite build --watch", "lint": "eslint .", "tsc": "tsc --pretty", diff --git a/oberoncms/adapter-turso/scripts/prepare.mjs b/oberoncms/adapter-turso/scripts/prepare.mjs index a8494206..1e8f30d5 100755 --- a/oberoncms/adapter-turso/scripts/prepare.mjs +++ b/oberoncms/adapter-turso/scripts/prepare.mjs @@ -79,7 +79,7 @@ const getClient = () => { const regex = /"className":"([^"]*)"/gm await mkdir(".oberon", { recursive: true }) - var outFile = await createWriteStream(".oberon/tailwind.classes") + var outFile = createWriteStream(".oberon/tailwind.classes") for (const { data } of results) { if (data) { diff --git a/oberoncms/adapter-turso/src/db/migrations/0002_redundant_triton.sql b/oberoncms/adapter-turso/src/db/migrations/0002_redundant_triton.sql new file mode 100644 index 00000000..0e338f17 --- /dev/null +++ b/oberoncms/adapter-turso/src/db/migrations/0002_redundant_triton.sql @@ -0,0 +1,2 @@ +ALTER TABLE images ADD `updated_at` integer NOT NULL;--> statement-breakpoint +ALTER TABLE images ADD `updated_by` text NOT NULL; \ No newline at end of file diff --git a/oberoncms/adapter-turso/src/db/migrations/meta/0002_snapshot.json b/oberoncms/adapter-turso/src/db/migrations/meta/0002_snapshot.json new file mode 100644 index 00000000..f7967ceb --- /dev/null +++ b/oberoncms/adapter-turso/src/db/migrations/meta/0002_snapshot.json @@ -0,0 +1,344 @@ +{ + "version": "5", + "dialect": "sqlite", + "id": "d86c34cd-b814-44a1-afca-f6b0d0d00969", + "prevId": "d3445c77-583d-4a2b-bbd0-9574e328de3c", + "tables": { + "account": { + "name": "account", + "columns": { + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "providerAccountId": { + "name": "providerAccountId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "token_type": { + "name": "token_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "session_state": { + "name": "session_state", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "account_userId_user_id_fk": { + "name": "account_userId_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "account_provider_providerAccountId_pk": { + "columns": [ + "provider", + "providerAccountId" + ], + "name": "account_provider_providerAccountId_pk" + } + }, + "uniqueConstraints": {} + }, + "session": { + "name": "session", + "columns": { + "sessionToken": { + "name": "sessionToken", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires": { + "name": "expires", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "session_userId_user_id_fk": { + "name": "session_userId_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "user": { + "name": "user", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "emailVerified": { + "name": "emailVerified", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'user'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "verificationToken": { + "name": "verificationToken", + "columns": { + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires": { + "name": "expires", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "verificationToken_identifier_token_pk": { + "columns": [ + "identifier", + "token" + ], + "name": "verificationToken_identifier_token_pk" + } + }, + "uniqueConstraints": {} + }, + "pages": { + "name": "pages", + "columns": { + "key": { + "name": "key", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "data": { + "name": "data", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "images": { + "name": "images", + "columns": { + "key": { + "name": "key", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "alt": { + "name": "alt", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "height": { + "name": "height", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "width": { + "name": "width", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_by": { + "name": "updated_by", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + } +} \ No newline at end of file diff --git a/oberoncms/adapter-turso/src/db/migrations/meta/_journal.json b/oberoncms/adapter-turso/src/db/migrations/meta/_journal.json index 0d84b095..c5f49958 100644 --- a/oberoncms/adapter-turso/src/db/migrations/meta/_journal.json +++ b/oberoncms/adapter-turso/src/db/migrations/meta/_journal.json @@ -15,6 +15,13 @@ "when": 1713015107722, "tag": "0001_chief_paper_doll", "breakpoints": true + }, + { + "idx": 2, + "version": "5", + "when": 1715167518353, + "tag": "0002_redundant_triton", + "breakpoints": true } ] } \ No newline at end of file diff --git a/oberoncms/adapter-turso/src/db/schema/image-schema.ts b/oberoncms/adapter-turso/src/db/schema/image-schema.ts index 9f97b347..97aafc53 100644 --- a/oberoncms/adapter-turso/src/db/schema/image-schema.ts +++ b/oberoncms/adapter-turso/src/db/schema/image-schema.ts @@ -7,4 +7,6 @@ export const images = sqliteTable("images", { size: integer("size").notNull(), height: integer("height").notNull(), width: integer("width").notNull(), + updatedAt: integer("updated_at", { mode: "timestamp" }).notNull(), + updatedBy: text("updated_by").notNull(), }) diff --git a/oberoncms/adapter-turso/src/index.ts b/oberoncms/adapter-turso/src/index.ts index e394abed..d583b064 100644 --- a/oberoncms/adapter-turso/src/index.ts +++ b/oberoncms/adapter-turso/src/index.ts @@ -42,6 +42,8 @@ export const oberonAdapter: OberonDatabaseAdapter = { size: images.size, height: images.height, width: images.width, + updatedAt: images.updatedAt, + updatedBy: images.updatedBy, }) .from(images) .execute() diff --git a/oberoncms/adapter-vercel-postgres/package.json b/oberoncms/adapter-vercel-postgres/package.json index f919cbb4..7eeadd9f 100644 --- a/oberoncms/adapter-vercel-postgres/package.json +++ b/oberoncms/adapter-vercel-postgres/package.json @@ -30,7 +30,7 @@ "scripts": { "build": "vite build", "clean": "node ../../scripts/clean-package.mjs", - "db:migrations:create": "drizzle-kit generate:pg", + "generate:migrations": "drizzle-kit generate:pg", "dev": "vite build --watch", "lint": "eslint .", "tsc": "tsc --pretty", diff --git a/oberoncms/adapter-vercel-postgres/src/db/migrations/0001_clear_jazinda.sql b/oberoncms/adapter-vercel-postgres/src/db/migrations/0001_clear_jazinda.sql new file mode 100644 index 00000000..aceb0377 --- /dev/null +++ b/oberoncms/adapter-vercel-postgres/src/db/migrations/0001_clear_jazinda.sql @@ -0,0 +1,2 @@ +ALTER TABLE "images" ADD COLUMN "updated_at" timestamp NOT NULL;--> statement-breakpoint +ALTER TABLE "images" ADD COLUMN "updated_by" text NOT NULL; \ No newline at end of file diff --git a/oberoncms/adapter-vercel-postgres/src/db/migrations/meta/0001_snapshot.json b/oberoncms/adapter-vercel-postgres/src/db/migrations/meta/0001_snapshot.json new file mode 100644 index 00000000..831cb2e2 --- /dev/null +++ b/oberoncms/adapter-vercel-postgres/src/db/migrations/meta/0001_snapshot.json @@ -0,0 +1,334 @@ +{ + "id": "5e6d1674-316b-4fba-a016-48f5604ea3d6", + "prevId": "cb23a67c-ba88-4602-900c-8a7de2c834a2", + "version": "5", + "dialect": "pg", + "tables": { + "account": { + "name": "account", + "schema": "", + "columns": { + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "providerAccountId": { + "name": "providerAccountId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "token_type": { + "name": "token_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "session_state": { + "name": "session_state", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "account_userId_user_id_fk": { + "name": "account_userId_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "account_provider_providerAccountId_pk": { + "name": "account_provider_providerAccountId_pk", + "columns": [ + "provider", + "providerAccountId" + ] + } + }, + "uniqueConstraints": {} + }, + "session": { + "name": "session", + "schema": "", + "columns": { + "sessionToken": { + "name": "sessionToken", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires": { + "name": "expires", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_userId_user_id_fk": { + "name": "session_userId_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "emailVerified": { + "name": "emailVerified", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'user'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "verificationToken": { + "name": "verificationToken", + "schema": "", + "columns": { + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires": { + "name": "expires", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "verificationToken_identifier_token_pk": { + "name": "verificationToken_identifier_token_pk", + "columns": [ + "identifier", + "token" + ] + } + }, + "uniqueConstraints": {} + }, + "pages": { + "name": "pages", + "schema": "", + "columns": { + "key": { + "name": "key", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "data": { + "name": "data", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "pages_key_idx": { + "name": "pages_key_idx", + "columns": [ + "key" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "images": { + "name": "images", + "schema": "", + "columns": { + "key": { + "name": "key", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "alt": { + "name": "alt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "height": { + "name": "height", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "width": { + "name": "width", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_by": { + "name": "updated_by", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "images_key_idx": { + "name": "images_key_idx", + "columns": [ + "key" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "schemas": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/oberoncms/adapter-vercel-postgres/src/db/migrations/meta/_journal.json b/oberoncms/adapter-vercel-postgres/src/db/migrations/meta/_journal.json index 2a051cfa..aee31810 100644 --- a/oberoncms/adapter-vercel-postgres/src/db/migrations/meta/_journal.json +++ b/oberoncms/adapter-vercel-postgres/src/db/migrations/meta/_journal.json @@ -8,6 +8,13 @@ "when": 1714483569560, "tag": "0000_modern_iron_fist", "breakpoints": true + }, + { + "idx": 1, + "version": "5", + "when": 1715167518379, + "tag": "0001_clear_jazinda", + "breakpoints": true } ] } \ No newline at end of file diff --git a/oberoncms/adapter-vercel-postgres/src/db/schema/image-schema.ts b/oberoncms/adapter-vercel-postgres/src/db/schema/image-schema.ts index cc62151f..d99d8ddf 100644 --- a/oberoncms/adapter-vercel-postgres/src/db/schema/image-schema.ts +++ b/oberoncms/adapter-vercel-postgres/src/db/schema/image-schema.ts @@ -1,4 +1,10 @@ -import { pgTable, text, integer, uniqueIndex } from "drizzle-orm/pg-core" +import { + pgTable, + text, + integer, + uniqueIndex, + timestamp, +} from "drizzle-orm/pg-core" export const images = pgTable( "images", @@ -9,6 +15,8 @@ export const images = pgTable( size: integer("size").notNull(), height: integer("height").notNull(), width: integer("width").notNull(), + updatedAt: timestamp("updated_at", { mode: "date" }).notNull(), + updatedBy: text("updated_by").notNull(), }, (images) => { return { diff --git a/oberoncms/adapter-vercel-postgres/src/index.ts b/oberoncms/adapter-vercel-postgres/src/index.ts index f27af84b..48439d0d 100644 --- a/oberoncms/adapter-vercel-postgres/src/index.ts +++ b/oberoncms/adapter-vercel-postgres/src/index.ts @@ -45,6 +45,8 @@ export const databaseAdapter: OberonDatabaseAdapter = { size: images.size, height: images.height, width: images.width, + updatedAt: images.updatedAt, + updatedBy: images.updatedBy, }) .from(images) .execute() diff --git a/oberoncms/core/src/adapter.tsx b/oberoncms/core/src/adapter.tsx index b0d4029e..ed4d9692 100644 --- a/oberoncms/core/src/adapter.tsx +++ b/oberoncms/core/src/adapter.tsx @@ -5,12 +5,12 @@ import { } from "next/cache" import { type Data } from "@measured/puck" import { + AddImageSchema, AddUserSchema, ChangeRoleSchema, DeletePageSchema, DeleteUserSchema, INITIAL_DATA, - ImageSchema, PageSchema, type AdapterActionGroup, type AdapterPermission, @@ -202,7 +202,7 @@ export function initAdapter({ addImage: async function (data: unknown) { await will("images", "write") - const image = ImageSchema.parse(data) + const image = AddImageSchema.parse(data) await db.addImage(image) revalidateTag("oberon-images") return db.getAllImages() diff --git a/oberoncms/core/src/app/schema.ts b/oberoncms/core/src/app/schema.ts index 31e56fbd..056196df 100644 --- a/oberoncms/core/src/app/schema.ts +++ b/oberoncms/core/src/app/schema.ts @@ -17,14 +17,14 @@ export const INITIAL_DATA = { root: { props: { title: "" } }, } satisfies Data -const MaybeOptimistic = z.object({ - pending: z.boolean().optional(), -}) +export type MaybeOptimistic = T & { + pending?: boolean +} /* * Pages */ -export const PageSchema = MaybeOptimistic.extend({ +export const PageSchema = z.object({ key: z .string() .regex(/^[0-9a-zA-Z_.-/]+$/, "Valid characters: 0-9 a-z A-Z -_./") @@ -38,31 +38,37 @@ export const PublishPageSchema = PageSchema.pick({ key: true }).extend({ }) // Cannot infer from zod because we need nextjs to understand key is a valid Route -export type OberonPage = z.infer & { - key: Route -} +export type OberonPage = MaybeOptimistic< + z.infer & { + key: Route + } +> /* * Images */ -export const ImageSchema = MaybeOptimistic.extend({ +export const ImageSchema = z.object({ key: z.string(), url: z.string(), size: z.number(), width: z.number().gt(0), height: z.number().gt(0), alt: z.string(), + updatedAt: z.date(), + updatedBy: z.string(), }) +export const AddImageSchema = ImageSchema + export const DeleteImageSchema = ImageSchema.pick({ key: true }) -export type OberonImage = z.infer +export type OberonImage = MaybeOptimistic> /* * Users */ -export const UserSchema = MaybeOptimistic.extend({ +export const UserSchema = z.object({ id: z.string(), email: z.string().email(), role: z.enum(["user", "admin"]), @@ -72,7 +78,7 @@ export const AddUserSchema = UserSchema.pick({ email: true, role: true }) export const ChangeRoleSchema = UserSchema.pick({ id: true, role: true }) export const DeleteUserSchema = UserSchema.pick({ id: true }) -export type OberonUser = z.infer +export type OberonUser = MaybeOptimistic> export const roles = ["user", "admin"] as const @@ -102,7 +108,7 @@ export type OberonDatabaseAdapter = { changeRole: (data: z.infer) => Promise getAllUsers: () => Promise getAllImages: () => Promise - addImage: (image: OberonImage) => Promise + addImage: (data: z.infer) => Promise deleteImage: (key: OberonImage["key"]) => Promise // TODO uploadthing addPage: (page: OberonPage & { data: string }) => Promise deletePage: (key: OberonPage["key"]) => Promise @@ -121,7 +127,7 @@ export type OberonActions = { ) => Promise | null> getAllUsers: () => Promise getAllImages: () => Promise - addImage: (image: OberonImage) => Promise + addImage: (data: z.infer) => Promise deleteImage: (key: OberonImage["key"]) => Promise // TODO uploadthing addPage: (page: OberonPage) => Promise deletePage: (data: z.infer) => Promise diff --git a/oberoncms/core/src/app/utils.ts b/oberoncms/core/src/app/utils.ts index b478f565..f4a06567 100644 --- a/oberoncms/core/src/app/utils.ts +++ b/oberoncms/core/src/app/utils.ts @@ -8,7 +8,7 @@ export function getTitle(action: ClientAction, slug?: string) { case "preview": return "Previewing: " + slug case "images": - return "Manage Assets" + return "Manage Images" case "users": return "Manage Users" case "pages": diff --git a/oberoncms/core/src/components/images.tsx b/oberoncms/core/src/components/images.tsx index 008bbbf1..086f7cc7 100644 --- a/oberoncms/core/src/components/images.tsx +++ b/oberoncms/core/src/components/images.tsx @@ -1,12 +1,18 @@ "use client" -import { Fragment, startTransition, useOptimistic } from "react" +import { + Fragment, + startTransition, + useOptimistic, + type PropsWithChildren, +} from "react" import { filesize } from "filesize" import Link from "next/link" import { Route } from "next" import { Button } from "@tohuhono/ui/button" import Image from "next/image" +import { format } from "@tohuhono/utils" import { useOberonActions } from "@/hooks/use-oberon" import type { OberonImage } from "@/app/schema" @@ -30,21 +36,31 @@ const useOberonImages = (images: OberonImage[]) => { } } +const ColumnHeading = ({ children }: PropsWithChildren) => + children ?
{children}
:
+ export function Images({ images: initialImages }: { images: OberonImage[] }) { const { images, deleteImage } = useOberonImages(initialImages) return ( -
- {images.map(({ key, alt, size, url, pending }) => { +
+ + Name + Size + Uploaded + By + + + {images.map(({ key, alt, size, updatedAt, updatedBy, url, pending }) => { return ( -
- {alt} - {alt} -
+ {alt} +
{alt}
{filesize(size)}
+
{format(updatedAt)}
+
{updatedBy}