Skip to content

Commit

Permalink
Merge pull request #71 from tawaks/feature/add-hobbies
Browse files Browse the repository at this point in the history
[Feature] Add Hobbies (#55)
  • Loading branch information
s1lvax authored Oct 23, 2024
2 parents d3e7073 + 8e8f236 commit 16c39c6
Show file tree
Hide file tree
Showing 12 changed files with 220 additions and 4 deletions.
15 changes: 15 additions & 0 deletions prisma/migrations/20241022160425_add_hobbies/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-- AlterTable
ALTER TABLE "User" ADD COLUMN "openToCollaborating" BOOLEAN NOT NULL DEFAULT false;

-- CreateTable
CREATE TABLE "Hobby" (
"id" SERIAL NOT NULL,
"userId" INTEGER NOT NULL,
"hobby" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,

CONSTRAINT "Hobby_pkey" PRIMARY KEY ("id")
);

-- AddForeignKey
ALTER TABLE "Hobby" ADD CONSTRAINT "Hobby_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("githubId") ON DELETE RESTRICT ON UPDATE CASCADE;
3 changes: 3 additions & 0 deletions prisma/migrations/migration_lock.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (i.e. Git)
provider = "postgresql"
10 changes: 10 additions & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ model User {
updatedAt DateTime @updatedAt
Link Link[]
Skill Skill[]
Hobby Hobby[]
}

model Link {
Expand All @@ -46,3 +47,12 @@ model Skill {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}

model Hobby {
id Int @id @default(autoincrement())
userId Int
hobby String
createdAt DateTime @default(now())
postedBy User @relation(fields: [userId], references: [githubId])
}
33 changes: 33 additions & 0 deletions src/lib/components/MyProfile/HobbyForm.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<script lang="ts">
import * as Form from '$lib/components/ui/form';
import { Input } from '$lib/components/ui/input';
import { hobbiesSchema, type HobbiesSchema } from '$lib/schemas/hobbies';
import { type SuperValidated, type Infer, superForm } from 'sveltekit-superforms';
import { zodClient } from 'sveltekit-superforms/adapters';
export let data: SuperValidated<Infer<HobbiesSchema>>;
const form = superForm(data, {
validators: zodClient(hobbiesSchema),
resetForm: true,
});
const { form: formData, enhance } = form;
</script>

<form
method="POST"
use:enhance
action="?/createHobby"
class="flex items-center justify-between space-x-4">
<div class="flex items-end space-x-2">
<Form.Field {form} name="hobby">
<Form.Control let:attrs>
<Form.Label>Hobby</Form.Label>
<Input {...attrs} bind:value={$formData.hobby} />
</Form.Control>
</Form.Field>
<Form.Button>Add</Form.Button>
</div>
</form>
28 changes: 28 additions & 0 deletions src/lib/components/MyProfile/UserHobbies.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<script lang="ts">
import { Heart,Trash2 } from 'lucide-svelte';
import { Button } from '$lib/components/ui/button';
import { enhance } from '$app/forms';
import { confirmDelete } from '$lib/utils/confirmDelete';
import type { Hobby } from '@prisma/client';
export let hobbies: Hobby[];
</script>

<div class="grid gap-4">
{#each hobbies as hobby}
<div class="flex items-center gap-4">
<Heart />
<div class="grid gap-1">
<p class="text-sm font-medium leading-none">{hobby.hobby}</p>
</div>
<div class="ml-auto font-medium">
<form action="?/deleteHobby&id={hobby.id}" method="POST" use:enhance>
<Button type="submit" variant="ghost" on:click={confirmDelete}>
<Trash2 />
</Button>
</form>
</div>
</div>
{/each}
</div>
35 changes: 35 additions & 0 deletions src/lib/components/PublicProfile/Hobbies.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<script lang="ts">
import * as Table from '$lib/components/ui/table';
import * as Card from '$lib/components/ui/card';
import type { PublicProfile } from '$lib/types/PublicProfile';
export let userData: PublicProfile;
</script>

<Card.Root>
<Card.Header>
<Card.Title>Hobbies</Card.Title>
<Card.Description>Discover the developer's hobbies</Card.Description>
</Card.Header>
<Card.Content class="grid gap-4">
{#if userData.hobbies.length > 0}
<Table.Root>
<Table.Header>
<Table.Row>
<Table.Head>Hobby</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
{#each userData.hobbies as hobby}
<Table.Row class="flex flex-row space-x-4 hover:cursor-pointer">
<Table.Cell class="font-medium">{hobby.hobby}</Table.Cell>
</Table.Row>
{/each}
</Table.Body>
</Table.Root>
{:else}
<p class="text-muted-foreground">No hobbies available</p>
{/if}
</Card.Content>
</Card.Root>

5 changes: 5 additions & 0 deletions src/lib/components/PublicProfile/PublicProfile.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import Links from '$lib/components/PublicProfile/Links.svelte';
import TechStack from '$lib/components/PublicProfile/TechStack.svelte';
import ProfileFooter from '$lib/components/PublicProfile/ProfileFooter.svelte';
import Hobbies from './Hobbies.svelte';
// Accept userData as a prop
export let userData: PublicProfile;
Expand All @@ -26,6 +27,10 @@
<Links {userData} />
<TechStack {userData} />
</div>
<div class="mt-8 grid gap-4 md:grid-cols-2">
<Hobbies {userData} />
</div>

<div class="flex flex-col items-center justify-center gap-4">
<ProfileFooter />
</div>
Expand Down
7 changes: 7 additions & 0 deletions src/lib/schemas/hobbies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { z } from 'zod';

export const hobbiesSchema = z.object({
hobby: z.string().min(1).max(100),
});

export type HobbiesSchema = typeof hobbiesSchema;
1 change: 1 addition & 0 deletions src/lib/types/PublicProfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ export interface PublicProfile {
skills: Array<{ title: string; level: string }>;
username: string;
isOpenToCollaborating: boolean;
hobbies: Array<{hobby:string}>;
}
7 changes: 6 additions & 1 deletion src/routes/[username]/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,16 @@ export const load: PageServerLoad = async ({ params }) => {
select: { openToCollaborating: true }
});

const hobbies = await prisma.hobby.findMany({
where:{userId: user.githubId},
});

const userData = {
links,
skills,
username: username,
isOpenToCollaborating: isOpenToCollaborating?.openToCollaborating
isOpenToCollaborating: isOpenToCollaborating?.openToCollaborating,
hobbies,
};

return {
Expand Down
67 changes: 64 additions & 3 deletions src/routes/profile/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type { User } from '$lib/types/User';
import { skillsSchema } from '$lib/schemas/skills';
import { deleteUser } from '$lib/utils/deleteUser';
import { updateOpenToCollaborating } from '$lib/utils/updateOpenToCollaborating';
import { hobbiesSchema } from '$lib/schemas/hobbies';

// Define the user variable with a possible null
let user: User | null = null;
Expand Down Expand Up @@ -44,7 +45,7 @@ export const load: PageServerLoad = async (event) => {
// Ensure user is not null before accessing properties
if (!user) throw new Error('User creation failed or user is null');

// Fetch links and skills related to the user
// Fetch links, skills and hobbies related to the user
const links = await prisma.link.findMany({
where: { userId: user.githubId },
orderBy: [{ order: 'asc' }]
Expand All @@ -55,6 +56,10 @@ export const load: PageServerLoad = async (event) => {
orderBy: [{ order: 'asc' }]
});

const hobbies = await prisma.hobby.findMany({
where: { userId: user.githubId },
});

// Create userStats object
const userData = {
username: user.githubUsername,
Expand All @@ -65,15 +70,18 @@ export const load: PageServerLoad = async (event) => {
// Initialize forms using superValidate
const linksForm = await superValidate(zod(linksSchema));
const skillsForm = await superValidate(zod(skillsSchema));
const hobbiesForm = await superValidate(zod(hobbiesSchema))

// Return data to the frontend
return {
userId: user.id,
userData,
links,
skills,
hobbies,
form: linksForm,
skillsForm: skillsForm
skillsForm: skillsForm,
hobbiesForm: hobbiesForm,
};
};

Expand Down Expand Up @@ -228,5 +236,58 @@ export const actions: Actions = {
throw Error('Failed to delete user');
}
}
}
},
createHobby: async (event) => {
const form = await superValidate(event, zod(hobbiesSchema));
if (!form.valid) {
return fail(400, {
form
});
}

// If no errors, get data
const { hobby } = form.data;

if (user) {
try {
await prisma.hobby.create({
data: {
hobby,
userId: user.githubId,
}
});
} catch (error) {
console.error(error);
throw Error('Failed to create hobby');
}
}

return {
form
};
},
deleteHobby: async ({ url, locals }) => {
//get hobby id from url
const id = url.searchParams.get('id');

//if no id found, return error
if (!id) {
return fail(400, { message: 'Invalid request' });
}

//delete hobby
try {
if (user) {
await prisma.hobby.delete({
where: {
id: Number(id),
userId: user.githubId
}
});
}
} catch (err) {
console.log(err);
return fail(500, { message: 'Something went wrong.' });
}
}
};
13 changes: 13 additions & 0 deletions src/routes/profile/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import { onMount } from 'svelte';
import type { PrivateProfileData } from '$lib/types/PrivateProfileData';
import UserSettings from '$lib/components/MyProfile/UserSettings.svelte';
import HobbyForm from '$lib/components/MyProfile/HobbyForm.svelte';
import UserHobbies from '$lib/components/MyProfile/UserHobbies.svelte';
let githubData: GithubData | null = null;
let privateProfileData: PrivateProfileData | null = null;
Expand Down Expand Up @@ -73,6 +75,17 @@
<UserSkills skills={data.skills} />
</Card.Content>
</Card.Root>

<Card.Root>
<Card.Header>
<Card.Title>Hobbies</Card.Title>
<Card.Description>You can add your hobbies here</Card.Description>
<HobbyForm data={data.hobbiesForm}/>
</Card.Header>
<Card.Content class="grid gap-8">
<UserHobbies hobbies={data.hobbies} />
</Card.Content>
</Card.Root>
</div>
</main>
</div>

0 comments on commit 16c39c6

Please sign in to comment.