mirror of
https://github.com/khairul169/code-share.git
synced 2026-10-24 11:53:58 +07:00
feat: add personal project list, add project settings, etc
This commit is contained in:
+2
-14
@@ -3,6 +3,7 @@ import type { Data } from "./+data";
|
||||
import { useData } from "~/renderer/hooks";
|
||||
import Link from "~/renderer/link";
|
||||
import Footer from "~/components/containers/footer";
|
||||
import ProjectCard from "~/components/containers/project-card";
|
||||
|
||||
const HomePage = () => {
|
||||
const { projects } = useData<Data>();
|
||||
@@ -35,20 +36,7 @@ const HomePage = () => {
|
||||
|
||||
<div className="grid sm:grid-cols-2 md:grid-cols-3 gap-3 md:gap-4">
|
||||
{projects.map((project) => (
|
||||
<Link
|
||||
key={project.id}
|
||||
href={`/${project.slug}`}
|
||||
className="border border-white/20 hover:border-white/40 rounded-lg transition-colors overflow-hidden"
|
||||
>
|
||||
<div className="w-full aspect-[3/2] bg-gray-900"></div>
|
||||
<div className="py-2 px-3 flex items-center gap-3">
|
||||
<div className="size-8 rounded-full bg-white/80"></div>
|
||||
<div>
|
||||
<p className="text-md truncate">{project.title}</p>
|
||||
<p className="text-xs truncate">{project.user.name}</p>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
<ProjectCard key={project.id} project={project} />
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -12,6 +12,7 @@ import Divider from "~/components/ui/divider";
|
||||
import trpc from "~/lib/trpc";
|
||||
import { useAuth } from "~/hooks/useAuth";
|
||||
import { usePageContext } from "~/renderer/context";
|
||||
import { useMemo } from "react";
|
||||
|
||||
const schema = z.object({
|
||||
forkFromId: z.number(),
|
||||
@@ -25,16 +26,21 @@ const schema = z.object({
|
||||
.optional(),
|
||||
});
|
||||
|
||||
const initialValue: z.infer<typeof schema> = {
|
||||
forkFromId: 0,
|
||||
title: "",
|
||||
};
|
||||
type Schema = z.infer<typeof schema>;
|
||||
|
||||
const GetStartedPage = () => {
|
||||
const { presets } = useData<Data>();
|
||||
const { presets, forkFrom } = useData<Data>();
|
||||
const { isLoggedIn } = useAuth();
|
||||
const ctx = usePageContext();
|
||||
|
||||
const initialValue: Schema = useMemo(
|
||||
() => ({
|
||||
forkFromId: forkFrom?.id || 0,
|
||||
title: forkFrom?.title || "",
|
||||
}),
|
||||
[forkFrom]
|
||||
);
|
||||
|
||||
const form = useForm(schema, initialValue);
|
||||
const create = trpc.project.create.useMutation({
|
||||
onSuccess(data) {
|
||||
@@ -49,31 +55,37 @@ const GetStartedPage = () => {
|
||||
return (
|
||||
<div className="container max-w-3xl min-h-[80dvh] flex flex-col items-center justify-center py-8 md:py-16">
|
||||
<Card className="w-full md:p-8">
|
||||
<CardTitle>Create New Project</CardTitle>
|
||||
<CardTitle>{(forkFrom ? "Fork" : "Create New") + " Project"}</CardTitle>
|
||||
|
||||
<form onSubmit={onSubmit}>
|
||||
<FormLabel>Select Preset</FormLabel>
|
||||
{!forkFrom ? (
|
||||
<>
|
||||
<FormLabel>Select Preset</FormLabel>
|
||||
|
||||
<Controller
|
||||
control={form.control}
|
||||
name="forkFromId"
|
||||
render={({ field }) => (
|
||||
<div className="flex md:grid md:grid-cols-3 gap-4 overflow-x-auto md:overflow-x-hidden">
|
||||
{presets.map((preset) => (
|
||||
<Button
|
||||
key={preset.projectId}
|
||||
variant={
|
||||
field.value === preset.projectId ? "default" : "outline"
|
||||
}
|
||||
className="flex py-16 border border-white/40 shrink-0 w-[160px] md:w-auto"
|
||||
onClick={() => field.onChange(preset.projectId)}
|
||||
>
|
||||
<p className="text-wrap">{preset.title}</p>
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={form.control}
|
||||
name="forkFromId"
|
||||
render={({ field }) => (
|
||||
<div className="flex md:grid md:grid-cols-3 gap-4 overflow-x-auto md:overflow-x-hidden">
|
||||
{presets.map((preset) => (
|
||||
<Button
|
||||
key={preset.projectId}
|
||||
variant={
|
||||
field.value === preset.projectId
|
||||
? "default"
|
||||
: "outline"
|
||||
}
|
||||
className="flex py-16 border border-white/40 shrink-0 w-[160px] md:w-auto"
|
||||
onClick={() => field.onChange(preset.projectId)}
|
||||
>
|
||||
<p className="text-wrap">{preset.title}</p>
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
|
||||
<Input
|
||||
form={form}
|
||||
|
||||
@@ -1,11 +1,30 @@
|
||||
import { and, eq, isNull } from "drizzle-orm";
|
||||
import { PageContext } from "vike/types";
|
||||
import trpcServer from "~/server/api/trpc/trpc";
|
||||
import db from "~/server/db";
|
||||
import { ProjectSchema, project } from "~/server/db/schema/project";
|
||||
import { UserSchema } from "~/server/db/schema/user";
|
||||
|
||||
export const data = async (ctx: PageContext) => {
|
||||
const trpc = await trpcServer(ctx);
|
||||
let forkFrom:
|
||||
| (Pick<ProjectSchema, "id" | "title"> & { user: UserSchema })
|
||||
| undefined;
|
||||
|
||||
if (ctx.urlParsed.search["fork"]) {
|
||||
forkFrom = await db.query.project.findFirst({
|
||||
columns: { id: true, title: true },
|
||||
where: and(
|
||||
eq(project.slug, ctx.urlParsed.search["fork"]),
|
||||
isNull(project.deletedAt)
|
||||
),
|
||||
with: { user: { columns: { password: false } } },
|
||||
});
|
||||
}
|
||||
|
||||
const presets = await trpc.project.getTemplates();
|
||||
|
||||
return { presets };
|
||||
return { presets, forkFrom };
|
||||
};
|
||||
|
||||
export type Data = Awaited<ReturnType<typeof data>>;
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import React from "react";
|
||||
import ProjectCard from "~/components/containers/project-card";
|
||||
import trpc from "~/lib/trpc";
|
||||
|
||||
const ProjectsPage = () => {
|
||||
const { data: projects, isLoading } = trpc.project.getAll.useQuery({
|
||||
owned: true,
|
||||
});
|
||||
|
||||
return (
|
||||
<main>
|
||||
<section id="browse" className="container max-w-5xl py-8 md:py-16">
|
||||
<h2 className="text-4xl font-medium border-b pb-3 mb-8 border-white/20">
|
||||
My Projects
|
||||
</h2>
|
||||
|
||||
<div className="grid sm:grid-cols-2 md:grid-cols-3 gap-3 md:gap-4">
|
||||
{isLoading
|
||||
? [...Array(6)].map((_, idx) => <ProjectCard.Skeleton key={idx} />)
|
||||
: null}
|
||||
|
||||
{projects?.map((project) => (
|
||||
<ProjectCard key={project.id} project={project} />
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProjectsPage;
|
||||
Reference in New Issue
Block a user