No description
  • Svelte 56.9%
  • TypeScript 39.5%
  • JavaScript 2.5%
  • HTML 1.1%
Find a file
2025-11-19 09:14:06 +00:00
assets Upload files to "assets" 2025-11-19 09:14:06 +00:00
e2e week 10 lab 2025-11-18 22:05:55 +00:00
src week 10 lab 2025-11-18 22:05:55 +00:00
static week 10 lab 2025-11-18 22:05:55 +00:00
.env.example week 10 lab 2025-11-18 22:05:55 +00:00
.gitignore week 10 lab 2025-11-18 22:05:55 +00:00
.npmrc week 10 lab 2025-11-18 22:05:55 +00:00
.prettierignore week 10 lab 2025-11-18 22:05:55 +00:00
.prettierrc week 10 lab 2025-11-18 22:05:55 +00:00
contact.sql week 10 lab 2025-11-18 22:05:55 +00:00
drizzle.config.ts week 10 lab 2025-11-18 22:05:55 +00:00
local.db.bak week 10 lab 2025-11-18 22:05:55 +00:00
package-lock.json week 10 lab 2025-11-18 22:05:55 +00:00
package.json week 10 lab 2025-11-18 22:05:55 +00:00
playwright.config.ts week 10 lab 2025-11-18 22:05:55 +00:00
README.md week 10 lab 2025-11-18 22:05:55 +00:00
recipes.sql week 10 lab 2025-11-18 22:05:55 +00:00
svelte.config.js week 10 lab 2025-11-18 22:05:55 +00:00
tsconfig.json week 10 lab 2025-11-18 22:05:55 +00:00
vite.config.ts week 10 lab 2025-11-18 22:05:55 +00:00

Lab 10: Review of CA1 + Practice

Enda Lee 2025

PDF version

Getting Started

  1. Clone or download this repository.
  2. Open the app folder in Visual Studio Code.
  3. Create a .env file in the application root using .env.example as a template.
  4. Rename local.db.bak to local.db
  5. In the terminal, run npm install to install dependencies.
  6. Start the development server with npm run dev.

Overview

This lab reviews the CA1 solutions.

1. Common setup

1.1 Application setup

  1. Routes setup, see: src/routes.
  2. BootStrap is imported in the shared +layout.svelte

1.2 Database and Schema

  1. Database and schema setup, see: src/lib/server/db/schema.ts. Note the Type exports.
  2. Requires npm run db:push to create the table(s) and execution of sample data SQL, e.g. in DB Browser.
  3. Data access layer functions, see: src/lib/server/data-access
  4. Service layer functions, see: src/lib/server/services

2. Display Recipes

  1. Display data in a table: src/routes/recipes

  2. +page.server.ts: get recipes from the database and return

  3. +page.svelte: Display recipes in a table.

  4. Split the ingredients and instructions strings to form arrays. Then display each araay element in a list:

    <!-- Split ingredients by comma and display each as a list item -->
    {#each recipe.ingredients.split(',') as i}
      <li>{i}</li>
    {/each}
    
  5. Filter by difficulty:

    1. The selectedDifficulty $state variable stores the filtered.
    2. filteredRecipes is a filtered recipe list, derived from recipes .
    3. The event handling function handleFilterClick() is called when the filter buttons are clicked. It sets selectedDifficulty with the required difficulty level.

3. Display Contact messages

  1. Display data in a table: src/routes/viewcontacts

  2. +page.server.ts: get contacts from the database and return

  3. +page.svelte: Display contacts in a table.

  4. The event handling function handleFilterClick() is called when the filter buttons are clicked. It returns a list of contacts filtered by based on the status parameter value.

Column sorting

  1. In the example, the **createdAt (Date/ Time) column ** is sorted by default:
	// Holds table sort state.  Initialized to reflect table sorted by createdAt descending
	let sortBy = $state({ col: 'createdAt', ascending: false });
  1. The sort functions takes a column name as a parameter for sorting.
    1. If the column value matches sortBy.col. then reverse the current order.
    2. If this is a new column (not equal sortBy.col) then set sortBy.col = column and set sort ascending.
    3. The sort process makes a copy of contacts and uses the JavaScript Array sort() method.

4. Review of Forms

The solution also includes a form to add new contact messages (not part of the assessment). This can be found in src\routes\contact

1. The form is defined in src\routes\contact\+page.svelte See the code comments for details

contact form

2. The <form> tag sets some important attributes:

The form

3. The form input fields follow, this is the firstName input

firstName input

4. The server-side

When the form is submitted, the data is sent to the createContact action handler which can be found in src/routes/contact/+page.server.ts

form action

5. Form validation

Step 3 above uses the contactSevice to validate and add the contact message to the database.

const result = await contactService.createContact(contactData);

The service can be found at src/lib/server/services/contact-service.ts

It in turn validates using the Zod validation schema which is defined in src/lib/server/db/validation.ts. Here you will find the validation checks and error messages to be displayed if an input does not match requirments.

// src/lib/server/db/validation.ts
import { createInsertSchema, createSelectSchema } from 'drizzle-zod';
import { z } from 'zod';
import { recipe, contact } from './schema.js';

export const selectRecipeSchema = createSelectSchema(recipe);

export const selectContactSchema = createSelectSchema(contact);
export const insertContactSchema = createInsertSchema(contact, {
    firstName: z.string().min(1, 'First name is required'),
    lastName: z.string().min(1, 'Last name is required'),
    email: z.email('Invalid email'),
    subject: z.string().min(1, 'Subject is required'),
    message: z.string().min(1, 'Message is required'),
});

Exercises

  1. Add cuisine filtering for recipes.
  2. Add the required functionality to allow addition of new recipes to the database.
  3. Add the ability to delete contact messages.
  4. Require authenication to view contact messages, add contacts and messages, and delete messages. All users should have access to the recipes list and the contact form.

Enda Lee 2025