Skip to content

Commit

Permalink
feat: add project creation
Browse files Browse the repository at this point in the history
  • Loading branch information
thraizz committed Jan 27, 2024
1 parent 0c7b9cb commit 72d25a2
Show file tree
Hide file tree
Showing 7 changed files with 270 additions and 8 deletions.
13 changes: 11 additions & 2 deletions src/components/Layout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ import TopBar from "@/components/TopBar.vue";
<div class="bg-indigo-600 pb-32">
<TopBar />

<header class="py-10">
<div class="mx-auto h-20 max-w-7xl px-4 sm:px-6 lg:px-8">
<header
class="flex w-full flex-row justify-between px-4 py-10 sm:px-6 lg:px-8"
>
<div class="h-20 max-w-7xl">
<h1 class="text-3xl font-bold tracking-tight text-white">
{{ $route.meta.title }}
</h1>
Expand All @@ -23,6 +25,13 @@ import TopBar from "@/components/TopBar.vue";
<ArrowLeftIcon class="size-4" /> Back
</router-link>
</div>

<router-link
to="/projects/new"
class="button secondary block h-min w-min"
>
Post Project
</router-link>
</header>
</div>

Expand Down
8 changes: 8 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ const router = createRouter({
title: "Registration",
},
},
{
path: "/projects/new",
component: () => import("./views/NewProject.vue"),
meta: {
title: "Post Project",
showBack: true,
},
},
{
path: "/projects/:id",
component: () => import("./views/Project.vue"),
Expand Down
27 changes: 26 additions & 1 deletion src/projects.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { collection, doc, getDocs, setDoc } from "firebase/firestore";
import { addDoc, collection, doc, getDocs, setDoc } from "firebase/firestore";
import { defineStore } from "pinia";
import { ref } from "vue";

import { firestore } from "./firebase";
import { Project } from "./types";
import { ProjectCreationFormData } from "./views/NewProject.vue";

export const useProjectStore = defineStore("projects", () => {
const projects = ref<Project[]>([]);
Expand Down Expand Up @@ -58,9 +59,33 @@ export const useProjectStore = defineStore("projects", () => {
});
}

function addProject(values: ProjectCreationFormData, userUid: string) {
const projectCollection = collection(firestore, "projects");
addDoc(projectCollection, {
title: values.title,
description: values.description,
textContent: values.content,
links: values.links,
tags: values.tags,
projectId: "",
userId: userUid,
images: [],
upvotes: [],
createdAt: new Date(),
logo: "",
})
.then(() => {
refetch();
})
.catch((error) => {
console.error("Error writing document: ", error);
});
}

return {
projects,
refetch,
upvoteProject,
addProject,
};
});
5 changes: 4 additions & 1 deletion src/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,13 @@
@apply block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6;
}
.button {
@apply flex w-full justify-center rounded-md px-3 py-1.5 text-sm font-semibold leading-6 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600;
@apply flex justify-center whitespace-nowrap rounded-md px-3 py-1.5 text-sm font-semibold leading-6 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600;
&.primary {
@apply bg-indigo-600 text-white shadow-sm hover:bg-indigo-500;
}
&.secondary {
@apply bg-white text-indigo-600 shadow-sm hover:bg-indigo-50 hover:text-indigo-900;
}
&.outlined {
@apply border-2 border-indigo-600 text-indigo-600 shadow-sm hover:bg-indigo-100;
}
Expand Down
4 changes: 2 additions & 2 deletions src/views/Login.vue
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,9 @@ watch(
</div>

<div class="flex flex-col gap-2">
<button type="submit" class="button primary">Sign in</button>
<button type="submit" class="button primary w-full">Sign in</button>

<router-link to="/register" class="button outlined">
<router-link to="/register" class="button outlined w-full">
Register
</router-link>
</div>
Expand Down
217 changes: 217 additions & 0 deletions src/views/NewProject.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
<script setup lang="ts">
import { useField, useForm } from "vee-validate";
import { useRouter } from "vue-router";
import { string } from "yup";
import { useUser } from "@/components/user";
import { useProjectStore } from "@/projects";
export type ProjectCreationFormData = {
title: string;
description: string;
content: string;
links: string[];
tags: string[];
};
const { handleSubmit, resetForm } = useForm<ProjectCreationFormData>({
validationSchema: {
title: string().required(),
description: string().required(),
content: string().required(),
links: string().required(),
tags: string().required(),
},
initialValues: {
title: "",
description: "",
content: "",
links: [],
tags: [],
},
});
const router = useRouter();
const projectStore = useProjectStore();
const { user } = useUser();
const onSubmit = handleSubmit(
// Success
(values: ProjectCreationFormData) => {
if (!user.value) return;
// handle form submission here
projectStore.addProject(values, user.value.uid);
resetForm();
router.push("/");
},
// Failure
(errors) => {
console.log(errors);
},
);
const { value: title, errorMessage: titleError } = useField("title");
const { value: description, errorMessage: descriptionError } =
useField<string>("description");
const { value: content, errorMessage: contentError } =
useField<string>("content");
const { value: links, errorMessage: linksError } = useField<string[]>("links");
const { value: tags, errorMessage: tagsError } = useField<string[]>("tags");
</script>

<template>
<form class="space-y-8 divide-y divide-gray-200" @submit="onSubmit">
<div class="space-y-8 divide-y divide-gray-200 sm:space-y-5">
<div>
<div>
<h3 class="text-lg font-medium leading-6 text-gray-900">
New Project
</h3>

<p class="mt-1 max-w-2xl text-sm text-gray-500">
Please fill in the form below to add a new project.
</p>
</div>

<div class="mt-6 space-y-6 sm:mt-5 sm:space-y-5">
<div
class="sm:grid sm:grid-cols-3 sm:items-start sm:gap-4 sm:border-t sm:border-gray-200 sm:pt-5"
>
<label
for="title"
class="block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2"
>
Title
</label>

<div class="mt-1 sm:col-span-2 sm:mt-0">
<input
id="title"
v-model="title"
type="text"
name="title"
autocomplete="title"
placeholder="The title of your project"
class="block w-full max-w-lg rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:max-w-xs sm:text-sm"
/>

<div class="mt-2 text-sm text-red-500">{{ titleError }}</div>
</div>
</div>

<div
class="sm:grid sm:grid-cols-3 sm:items-start sm:gap-4 sm:border-t sm:border-gray-200 sm:pt-5"
>
<label
for="description"
class="block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2"
>
Description
</label>

<div class="mt-1 sm:col-span-2 sm:mt-0">
<textarea
id="description"
v-model="description"
name="description"
rows="3"
placeholder="A short description of your project"
class="block w-full max-w-lg rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm"
></textarea>

<p class="error">{{ descriptionError }}</p>
</div>
</div>

<div
class="sm:grid sm:grid-cols-3 sm:items-start sm:gap-4 sm:border-t sm:border-gray-200 sm:pt-5"
>
<label
for="content"
class="block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2"
>
Content
</label>

<div class="mt-1 sm:col-span-2 sm:mt-0">
<textarea
id="content"
v-model="content"
name="content"
rows="3"
placeholder="A longer description of your project"
class="block w-full max-w-lg rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm"
></textarea>

<p class="error">{{ contentError }}</p>
</div>
</div>

<div
class="sm:grid sm:grid-cols-3 sm:items-start sm:gap-4 sm:border-t sm:border-gray-200 sm:pt-5"
>
<label
for="links"
class="block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2"
>
Links
</label>

<div class="mt-1 sm:col-span-2 sm:mt-0">
<input
id="links"
v-model="links"
name="links"
type="text"
autocomplete="links"
placeholder="Add a comma-separated list of links"
class="block w-full max-w-lg rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm"
/>

<p class="error">{{ linksError }}</p>
</div>
</div>

<div
class="sm:grid sm:grid-cols-3 sm:items-start sm:gap-4 sm:border-t sm:border-gray-200 sm:pt-5"
>
<label
for="tags"
class="block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2"
>
Tags
</label>

<div class="mt-1 sm:col-span-2 sm:mt-0">
<input
id="tags"
v-model="tags"
name="tags"
type="text"
autocomplete="tags"
placeholder="Add tags separated by commas"
class="block w-full max-w-lg rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm"
/>

<p class="error">{{ tagsError }}</p>
</div>
</div>
</div>
</div>
</div>

<div class="pt-5">
<div class="flex justify-end gap-2">
<button
type="button"
class="button secondary min-w-20"
@click="() => resetForm()"
>
Reset
</button>

<button type="submit" class="button primary min-w-20">Submit</button>
</div>
</div>
</form>
</template>
4 changes: 2 additions & 2 deletions src/views/Registration.vue
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,9 @@ watch(
</div>

<div class="flex flex-col gap-2">
<button type="submit" class="button primary">Register</button>
<button type="submit" class="button primary w-full">Register</button>

<router-link to="/login" class="button outlined">
<router-link to="/login" class="button outlined w-full">
Sign In
</router-link>
</div>
Expand Down

0 comments on commit 72d25a2

Please sign in to comment.