lib was accidentally being ignored
Some checks failed
CI/CD Pipeline / VM Test - backend-integration (push) Successful in 3s
CI/CD Pipeline / VM Test - full-stack (push) Successful in 3s
CI/CD Pipeline / VM Test - performance (push) Successful in 2s
CI/CD Pipeline / VM Test - security (push) Successful in 2s
CI/CD Pipeline / Backend Linting (push) Successful in 2s
CI/CD Pipeline / Frontend Linting (push) Failing after 12s
CI/CD Pipeline / Nix Flake Check (push) Successful in 37s
CI/CD Pipeline / CI Summary (push) Failing after 0s

This commit is contained in:
Danilo Reyes
2025-11-02 12:23:25 -06:00
parent 681fa0903b
commit c52ac86739
34 changed files with 4164 additions and 6 deletions

View File

@@ -0,0 +1,266 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte';
export let initialTitle: string = '';
export let initialDescription: string = '';
let title = initialTitle;
let description = initialDescription;
let errors: Record<string, string> = {};
const dispatch = createEventDispatcher<{
create: { title: string; description?: string };
close: void;
}>();
function validate(): boolean {
errors = {};
if (!title.trim()) {
errors.title = 'Title is required';
} else if (title.length > 255) {
errors.title = 'Title must be 255 characters or less';
}
if (description.length > 1000) {
errors.description = 'Description must be 1000 characters or less';
}
return Object.keys(errors).length === 0;
}
function handleSubmit() {
if (!validate()) return;
dispatch('create', {
title: title.trim(),
description: description.trim() || undefined,
});
}
function handleClose() {
dispatch('close');
}
function handleBackdropClick(event: MouseEvent) {
if (event.target === event.currentTarget) {
handleClose();
}
}
</script>
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
<div
class="modal-backdrop"
on:click={handleBackdropClick}
on:keydown={(e) => e.key === 'Escape' && handleClose()}
role="dialog"
aria-modal="true"
tabindex="-1"
>
<div class="modal-content">
<header class="modal-header">
<h2>Create New Board</h2>
<button class="close-btn" on:click={handleClose} aria-label="Close">×</button>
</header>
<form on:submit|preventDefault={handleSubmit} class="modal-form">
<div class="form-group">
<label for="title">Board Title <span class="required">*</span></label>
<input
id="title"
type="text"
bind:value={title}
placeholder="e.g., Character Design References"
class:error={errors.title}
maxlength="255"
required
/>
{#if errors.title}
<span class="error-text">{errors.title}</span>
{/if}
</div>
<div class="form-group">
<label for="description">Description (optional)</label>
<textarea
id="description"
bind:value={description}
placeholder="Add a description for this board..."
rows="3"
maxlength="1000"
class:error={errors.description}
/>
{#if errors.description}
<span class="error-text">{errors.description}</span>
{:else}
<span class="help-text">{description.length}/1000 characters</span>
{/if}
</div>
<div class="modal-actions">
<button type="button" class="btn-secondary" on:click={handleClose}>Cancel</button>
<button type="submit" class="btn-primary">Create Board</button>
</div>
</form>
</div>
</div>
<style>
.modal-backdrop {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
padding: 1rem;
}
.modal-content {
background: white;
border-radius: 16px;
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
width: 100%;
max-width: 500px;
max-height: 90vh;
overflow-y: auto;
}
.modal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 1.5rem;
border-bottom: 1px solid #e5e7eb;
}
.modal-header h2 {
font-size: 1.5rem;
font-weight: 600;
color: #1f2937;
margin: 0;
}
.close-btn {
background: none;
border: none;
font-size: 2rem;
color: #6b7280;
cursor: pointer;
padding: 0;
line-height: 1;
transition: color 0.2s;
}
.close-btn:hover {
color: #1f2937;
}
.modal-form {
padding: 1.5rem;
}
.form-group {
margin-bottom: 1.5rem;
}
.form-group:last-of-type {
margin-bottom: 2rem;
}
label {
display: block;
font-weight: 500;
color: #374151;
margin-bottom: 0.5rem;
font-size: 0.95rem;
}
.required {
color: #ef4444;
}
input,
textarea {
width: 100%;
padding: 0.75rem;
border: 1px solid #d1d5db;
border-radius: 8px;
font-size: 1rem;
font-family: inherit;
transition: all 0.2s;
}
input:focus,
textarea:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
input.error,
textarea.error {
border-color: #ef4444;
}
textarea {
resize: vertical;
min-height: 80px;
}
.error-text {
display: block;
color: #ef4444;
font-size: 0.875rem;
margin-top: 0.25rem;
}
.help-text {
display: block;
color: #6b7280;
font-size: 0.875rem;
margin-top: 0.25rem;
}
.modal-actions {
display: flex;
gap: 1rem;
justify-content: flex-end;
}
.btn-primary,
.btn-secondary {
padding: 0.75rem 1.5rem;
border-radius: 8px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
border: none;
}
.btn-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.btn-primary:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
}
.btn-secondary {
background: white;
color: #374151;
border: 1px solid #d1d5db;
}
.btn-secondary:hover {
background: #f9fafb;
}
</style>