Skip to content

validation and image proxy #7

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 83 additions & 4 deletions frontend/src/lib/components/CreateProject.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@
});
let events: Event[] = $state([]);
let fetchedEvents = false;
let formErrors = $state({
name: "",
description: "",
image_url: "",
event: "",
});
let submitting = $state(false);

async function fetchEvents() {
try {
Expand All @@ -29,8 +36,58 @@
}
}

async function createProject() {
function validate() {
let isValid = true;

formErrors = {
name: "",
description: "",
image_url: "",
event: "",
};

if (!project.name.trim()) {
formErrors.name = "Project name is required";
isValid = false;
}

if (!project.description.trim()) {
formErrors.description = "Project description is required";
isValid = false;
}

// img url check
if (project.image_url.trim()) {
const validExt = [".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg"];
const isValidExt = validExt.some((ext) =>
project.image_url.toLowerCase().endsWith(ext),
);

if (!isValidExt) {
formErrors.image_url =
"Image URL must end with a valid image extension (.jpg, .jpeg, .png, .gif, .webp, .svg)";
isValid = false;
}
}

if (!project.event[0]) {
formErrors.event = "Please select an event";
isValid = false;
}

return isValid;
}

async function createProject(e: Event) {
e.preventDefault();

if (!validate()) {
toast.error("Please fix the errors in the form");
return;
}

try {
submitting = true;
await ProjectsService.createProjectProjectsPost({
body: project,
throwOnError: true,
Expand All @@ -48,12 +105,14 @@
};
} catch (err) {
handleError(err);
} finally {
submitting = false;
}
}
</script>

<div class="p-4 max-w-md mx-auto">
<form onsubmit={createProject} class="space-y-4">
<form on:submit={createProject} class="space-y-4">
<label class="form-control">
<div class="label">
<span class="label-text">Project Name</span>
Expand All @@ -63,7 +122,11 @@
bind:value={project.name}
placeholder="A really cool project!"
class="input input-bordered grow"
class:input-error={formErrors.name}
/>
{#if formErrors.name}
<div class="text-error text-sm mt-1">{formErrors.name}</div>
{/if}
</label>
<!-- Project description field -->
<label class="form-control">
Expand All @@ -74,7 +137,11 @@
bind:value={project.description}
placeholder="Some cool description"
class="textarea textarea-bordered grow"
class:textarea-error={formErrors.description}
></textarea>
{#if formErrors.description}
<div class="text-error text-sm mt-1">{formErrors.description}</div>
{/if}
</label>
<label class="form-control">
<div class="label">
Expand All @@ -88,7 +155,11 @@
bind:value={project.image_url}
placeholder="Image URL"
class="input input-bordered grow"
class:input-error={formErrors.image_url}
/>
{#if formErrors.image_url}
<div class="text-error text-sm mt-1">{formErrors.image_url}</div>
{/if}
</label>
<label class="form-control">
<div class="label">
Expand Down Expand Up @@ -140,6 +211,7 @@
<select
bind:value={project.event[0]}
class="select select-bordered"
class:select-error={formErrors.event}
onfocus={() => {
if (!fetchedEvents) fetchEvents();
}}
Expand All @@ -149,9 +221,16 @@
<option value={event.id}>{event.name}</option>
{/each}
</select>
{#if formErrors.event}
<div class="text-error text-sm mt-1">{formErrors.event}</div>
{/if}
</label>
<button type="submit" class="btn btn-block btn-primary mt-4">
Create Project
<button
type="submit"
class="btn btn-block btn-primary mt-4"
disabled={submitting}
>
{submitting ? "Creating..." : "Create Project"}
</button>
</form>
</div>
2 changes: 2 additions & 0 deletions frontend/src/lib/components/JoinProject.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
<div class="label">Join Code</div>
<input
type="text"
minlength="4"
maxlength="4"
bind:value={toSend.query.join_code}
placeholder="~4 character case-insensitive join code"
class="w-full input input-bordered"
Expand Down
10 changes: 7 additions & 3 deletions frontend/src/lib/components/ProjectCard.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@

let { project, isSelected, toggle, selectable = false }: Props = $props();

function proxy(url: string): string {
return `https://wsrv.nl/?url=${encodeURIComponent(url)}&w=400&q=85`;
}

// $inspect(project);
</script>

Expand All @@ -31,18 +35,18 @@
: ''}"
>
<figure>
<img src={project.image_url} alt="Project" />
<img src={proxy(project.image_url)} alt="Project" loading="lazy" />
</figure>
<div class="card-body">
<h2 class="card-title">
{project.name}
</h2>
<p>{project.description}</p>
<div class="card-actions justify-end">
<a href={project.repo} target="_blank">
<a href={project.repo} target="_blank" referrerpolicy="no-referrer">
<div class="badge badge-secondary badge-lg underline">Repo</div>
</a>
<a href={project.demo} target="_blank">
<a href={project.demo} target="_blank" referrerpolicy="no-referrer">
<div class="badge badge-primary badge-lg underline">Demo</div>
</a>
</div>
Expand Down