shareable spaces

This commit is contained in:
Dhravya 2024-07-01 20:32:50 -05:00
parent 2c6a96e96f
commit 46dfc3b4e7
9 changed files with 958 additions and 18 deletions

View file

@ -30,17 +30,20 @@ import {
DropdownMenuTrigger,
} from "@repo/ui/shadcn/dropdown-menu";
import { Button } from "@repo/ui/shadcn/button";
import { deleteItem, moveItem } from "@/app/actions/doers";
import { addUserToSpace, deleteItem, moveItem } from "@/app/actions/doers";
import { toast } from "sonner";
import { Input } from "@repo/ui/shadcn/input";
export function MemoriesPage({
memoriesAndSpaces,
title = "Your Memories",
currentSpace,
usersWithAccess,
}: {
memoriesAndSpaces: { memories: Content[]; spaces: StoredSpace[] };
title?: string;
currentSpace?: StoredSpace;
usersWithAccess?: string[];
}) {
const [filter, setFilter] = useState("All");
@ -70,7 +73,6 @@ export function MemoriesPage({
.filter((item) => {
if (filter === "All") return true;
if (filter === "Spaces" && item.item === "space") {
console.log(item);
return true;
}
if (filter === "Pages")
@ -105,12 +107,55 @@ export function MemoriesPage({
{title}
</h2>
{currentSpace && (
<div className="flex gap-4 items-center">
Space
<div className="flex items-center gap-2 bg-secondary p-2 rounded-xl">
<Image src={MemoriesIcon} alt="Spaces icon" className="w-3 h-3" />
<span className="text-[#fff]">{currentSpace.name}</span>
<div className="flex flex-col gap-2">
<div className="flex gap-4 items-center">
Space
<div className="flex items-center gap-2 bg-secondary p-2 rounded-xl">
<Image src={MemoriesIcon} alt="Spaces icon" className="w-3 h-3" />
<span className="text-[#fff]">{currentSpace.name}</span>
</div>
</div>
{usersWithAccess && usersWithAccess.length > 0 && (
<div className="flex gap-4 items-center">
Users with access
<div className="flex gap-2">
{usersWithAccess.map((user) => (
<div className="flex items-center gap-2 bg-secondary p-2 rounded-xl">
<Image
src={UrlIcon}
alt="Spaces icon"
className="w-3 h-3"
/>
<span className="text-[#fff]">{user}</span>
</div>
))}
</div>
</div>
)}
<form
action={async (e: FormData) => {
const email = e.get("email")?.toString();
if (!email) {
toast.error("Please enter an email");
return;
}
const resp = await addUserToSpace(email, currentSpace.id);
if (resp.success) {
toast.success("User added to space");
} else {
toast.error("Failed to add user to space");
}
}}
className="flex gap-2 max-w-xl mt-2"
>
<Input name="email" placeholder="Add user by email" />
<Button variant="secondary">Add</Button>
</form>
</div>
)}
@ -268,7 +313,6 @@ function LinkComponent({
onClick={async () => {
toast.info("Adding to space...");
console.log(id, space.id);
const response = await moveItem(id, [space.id]);
if (response.success) {

View file

@ -1,15 +1,24 @@
import { getMemoriesInsideSpace } from "@/app/actions/fetchers";
import { redirect } from "next/navigation";
import MemoriesPage from "../../content";
import { db } from "@/server/db";
import { and, eq } from "drizzle-orm";
import { spacesAccess } from "@/server/db/schema";
async function Page({ params: { spaceid } }: { params: { spaceid: number } }) {
const { success, data } = await getMemoriesInsideSpace(spaceid);
if (!success ?? !data) return redirect("/home");
const hasAccess = await db.query.spacesAccess.findMany({
where: and(eq(spacesAccess.spaceId, spaceid)),
});
return (
<MemoriesPage
memoriesAndSpaces={{ memories: data.memories, spaces: [] }}
title={data.spaces[0]?.name}
currentSpace={data.spaces[0]}
usersWithAccess={hasAccess.map((x) => x.userEmail) ?? []}
/>
);
}

View file

@ -9,6 +9,7 @@ import {
contentToSpace,
sessions,
space,
spacesAccess,
storedContent,
users,
} from "../../server/db/schema";
@ -95,6 +96,47 @@ export const limit = async (
return items <= remainingLimit;
};
export const addUserToSpace = async (userEmail: string, spaceId: number) => {
const data = await auth();
if (!data || !data.user || !data.user.id) {
redirect("/signin");
return { error: "Not authenticated", success: false };
}
// We need to make sure that the user owns the space
const spaceData = await db
.select()
.from(space)
.where(and(eq(space.id, spaceId), eq(space.user, data.user.id)))
.all();
if (spaceData.length === 0) {
return {
success: false,
error: "You do not own this space",
};
}
try {
await db.insert(spacesAccess).values({
spaceId: spaceId,
userEmail: userEmail,
});
revalidatePath("/space/" + spaceId);
return {
success: true,
};
} catch (e) {
return {
success: false,
error: (e as Error).message,
};
}
};
const getTweetData = async (tweetID: string) => {
const url = `https://cdn.syndication.twimg.com/tweet-result?id=${tweetID}&lang=en&features=tfw_timeline_list%3A%3Btfw_follower_count_sunset%3Atrue%3Btfw_tweet_edit_backend%3Aon%3Btfw_refsrc_session%3Aon%3Btfw_fosnr_soft_interventions_enabled%3Aon%3Btfw_show_birdwatch_pivots_enabled%3Aon%3Btfw_show_business_verified_badge%3Aon%3Btfw_duplicate_scribes_to_settings%3Aon%3Btfw_use_profile_image_shape_enabled%3Aon%3Btfw_show_blue_verified_badge%3Aon%3Btfw_legacy_timeline_sunset%3Atrue%3Btfw_show_gov_verified_badge%3Aon%3Btfw_show_business_affiliate_badge%3Aon%3Btfw_tweet_edit_frontend%3Aon&token=4c2mmul6mnh`;
@ -191,8 +233,6 @@ export const createMemory = async (input: {
storeToSpaces = [];
}
console.log("SAVING URL: ", metadata.baseUrl);
const vectorSaveResponse = await fetch(
`${process.env.BACKEND_BASE_URL}/api/add`,
{

View file

@ -1,6 +1,6 @@
"use server";
import { and, asc, eq, inArray, not, sql } from "drizzle-orm";
import { and, asc, eq, inArray, not, or, sql } from "drizzle-orm";
import { db } from "../../server/db";
import {
canvas,
@ -10,6 +10,7 @@ import {
Content,
contentToSpace,
space,
spacesAccess,
storedContent,
StoredSpace,
users,
@ -90,12 +91,24 @@ export const getMemoriesInsideSpace = async (
.select()
.from(storedContent)
.where(
inArray(
storedContent.id,
db
.select({ contentId: contentToSpace.contentId })
.from(contentToSpace)
.where(eq(contentToSpace.spaceId, spaceId)),
and(
inArray(
storedContent.id,
db
.select({ contentId: contentToSpace.contentId })
.from(contentToSpace)
.where(eq(contentToSpace.spaceId, spaceId)),
),
or(
eq(storedContent.userId, data.user.id!),
eq(
db
.select({ userId: spacesAccess.userEmail })
.from(spacesAccess)
.where(eq(spacesAccess.spaceId, spaceId)),
data.user.email,
),
),
),
)
.execute();

View file

@ -0,0 +1,6 @@
CREATE TABLE `spacesAccess` (
`spaceId` integer NOT NULL,
`userEmail` text NOT NULL,
PRIMARY KEY(`spaceId`, `userEmail`),
FOREIGN KEY (`spaceId`) REFERENCES `space`(`id`) ON UPDATE no action ON DELETE cascade
);

View file

@ -0,0 +1,806 @@
{
"version": "6",
"dialect": "sqlite",
"id": "1c8dc08a-69e1-4d82-b4fc-e6df7b565864",
"prevId": "5d27a673-efd9-47a7-a9f5-84d5cc7d851d",
"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": {}
},
"authenticator": {
"name": "authenticator",
"columns": {
"credentialID": {
"name": "credentialID",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"userId": {
"name": "userId",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"providerAccountId": {
"name": "providerAccountId",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"credentialPublicKey": {
"name": "credentialPublicKey",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"counter": {
"name": "counter",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"credentialDeviceType": {
"name": "credentialDeviceType",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"credentialBackedUp": {
"name": "credentialBackedUp",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"transports": {
"name": "transports",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"authenticator_credentialID_unique": {
"name": "authenticator_credentialID_unique",
"columns": ["credentialID"],
"isUnique": true
}
},
"foreignKeys": {
"authenticator_userId_user_id_fk": {
"name": "authenticator_userId_user_id_fk",
"tableFrom": "authenticator",
"tableTo": "user",
"columnsFrom": ["userId"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"authenticator_userId_credentialID_pk": {
"columns": ["credentialID", "userId"],
"name": "authenticator_userId_credentialID_pk"
}
},
"uniqueConstraints": {}
},
"canvas": {
"name": "canvas",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"title": {
"name": "title",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'Untitled'"
},
"description": {
"name": "description",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'Untitled'"
},
"url": {
"name": "url",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "''"
},
"userId": {
"name": "userId",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {
"canvas_user_userId": {
"name": "canvas_user_userId",
"columns": ["userId"],
"isUnique": false
}
},
"foreignKeys": {
"canvas_userId_user_id_fk": {
"name": "canvas_userId_user_id_fk",
"tableFrom": "canvas",
"tableTo": "user",
"columnsFrom": ["userId"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"chatHistory": {
"name": "chatHistory",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"threadId": {
"name": "threadId",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"question": {
"name": "question",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"answerParts": {
"name": "answerParts",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"answerSources": {
"name": "answerSources",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"answerJustification": {
"name": "answerJustification",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"chatHistory_thread_idx": {
"name": "chatHistory_thread_idx",
"columns": ["threadId"],
"isUnique": false
}
},
"foreignKeys": {
"chatHistory_threadId_chatThread_id_fk": {
"name": "chatHistory_threadId_chatThread_id_fk",
"tableFrom": "chatHistory",
"tableTo": "chatThread",
"columnsFrom": ["threadId"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"chatThread": {
"name": "chatThread",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"firstMessage": {
"name": "firstMessage",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"userId": {
"name": "userId",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {
"chatThread_user_idx": {
"name": "chatThread_user_idx",
"columns": ["userId"],
"isUnique": false
}
},
"foreignKeys": {
"chatThread_userId_user_id_fk": {
"name": "chatThread_userId_user_id_fk",
"tableFrom": "chatThread",
"tableTo": "user",
"columnsFrom": ["userId"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"contentToSpace": {
"name": "contentToSpace",
"columns": {
"contentId": {
"name": "contentId",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"spaceId": {
"name": "spaceId",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"contentToSpace_contentId_storedContent_id_fk": {
"name": "contentToSpace_contentId_storedContent_id_fk",
"tableFrom": "contentToSpace",
"tableTo": "storedContent",
"columnsFrom": ["contentId"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
},
"contentToSpace_spaceId_space_id_fk": {
"name": "contentToSpace_spaceId_space_id_fk",
"tableFrom": "contentToSpace",
"tableTo": "space",
"columnsFrom": ["spaceId"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"contentToSpace_contentId_spaceId_pk": {
"columns": ["contentId", "spaceId"],
"name": "contentToSpace_contentId_spaceId_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": {}
},
"space": {
"name": "space",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'none'"
},
"user": {
"name": "user",
"type": "text(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"createdAt": {
"name": "createdAt",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"numItems": {
"name": "numItems",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": 0
}
},
"indexes": {
"space_name_unique": {
"name": "space_name_unique",
"columns": ["name"],
"isUnique": true
},
"spaces_name_idx": {
"name": "spaces_name_idx",
"columns": ["name"],
"isUnique": false
},
"spaces_user_idx": {
"name": "spaces_user_idx",
"columns": ["user"],
"isUnique": false
}
},
"foreignKeys": {
"space_user_user_id_fk": {
"name": "space_user_user_id_fk",
"tableFrom": "space",
"tableTo": "user",
"columnsFrom": ["user"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"spacesAccess": {
"name": "spacesAccess",
"columns": {
"spaceId": {
"name": "spaceId",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"userEmail": {
"name": "userEmail",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"spacesAccess_spaceId_space_id_fk": {
"name": "spacesAccess_spaceId_space_id_fk",
"tableFrom": "spacesAccess",
"tableTo": "space",
"columnsFrom": ["spaceId"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
"spacesAccess_spaceId_userEmail_pk": {
"columns": ["spaceId", "userEmail"],
"name": "spacesAccess_spaceId_userEmail_pk"
}
},
"uniqueConstraints": {}
},
"storedContent": {
"name": "storedContent",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"content": {
"name": "content",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"title": {
"name": "title",
"type": "text(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"description": {
"name": "description",
"type": "text(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"url": {
"name": "url",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"savedAt": {
"name": "savedAt",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"baseUrl": {
"name": "baseUrl",
"type": "text(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"ogImage": {
"name": "ogImage",
"type": "text(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"type": {
"name": "type",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false,
"default": "'page'"
},
"image": {
"name": "image",
"type": "text(255)",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"user": {
"name": "user",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"noteId": {
"name": "noteId",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"storedContent_baseUrl_unique": {
"name": "storedContent_baseUrl_unique",
"columns": ["baseUrl"],
"isUnique": true
},
"storedContent_url_idx": {
"name": "storedContent_url_idx",
"columns": ["url"],
"isUnique": false
},
"storedContent_savedAt_idx": {
"name": "storedContent_savedAt_idx",
"columns": ["savedAt"],
"isUnique": false
},
"storedContent_title_idx": {
"name": "storedContent_title_idx",
"columns": ["title"],
"isUnique": false
},
"storedContent_user_idx": {
"name": "storedContent_user_idx",
"columns": ["user"],
"isUnique": false
}
},
"foreignKeys": {
"storedContent_user_user_id_fk": {
"name": "storedContent_user_user_id_fk",
"tableFrom": "storedContent",
"tableTo": "user",
"columnsFrom": ["user"],
"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
},
"telegramId": {
"name": "telegramId",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
"users_email_idx": {
"name": "users_email_idx",
"columns": ["email"],
"isUnique": false
},
"users_telegram_idx": {
"name": "users_telegram_idx",
"columns": ["telegramId"],
"isUnique": false
},
"users_id_idx": {
"name": "users_id_idx",
"columns": ["id"],
"isUnique": false
}
},
"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": {}
}
},
"enums": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
}
}

View file

@ -29,6 +29,13 @@
"when": 1719798795179,
"tag": "0003_solid_boom_boom",
"breakpoints": true
},
{
"idx": 4,
"version": "6",
"when": 1719882966298,
"tag": "0004_skinny_stature",
"breakpoints": true
}
]
}

View file

@ -161,6 +161,21 @@ export const space = createTable(
}),
);
export const spacesAccess = createTable(
"spacesAccess",
{
spaceId: integer("spaceId")
.notNull()
.references(() => space.id, { onDelete: "cascade" }),
userEmail: text("userEmail").notNull(),
},
(spaceAccess) => ({
compoundKey: primaryKey({
columns: [spaceAccess.spaceId, spaceAccess.userEmail],
}),
}),
);
export type StoredContent = Omit<typeof storedContent.$inferSelect, "user">;
export type StoredSpace = typeof space.$inferSelect;
export type ChachedSpaceContent = StoredContent & {

View file

@ -11,7 +11,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
<input
type={type}
className={cn(
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
"flex h-10 w-full rounded-md border-2 border-border bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className,
)}
ref={ref}