deeper typescript integration

This commit is contained in:
2023-12-04 19:06:36 -06:00
parent ed4a8ea43d
commit 6476972508
7 changed files with 121 additions and 33 deletions

View File

@@ -1,5 +0,0 @@
module.exports = {
extends: [
'semistandard'
]
};

View File

@@ -1,12 +1,13 @@
@model IEnumerable<Recipe>
@{
string InputValue = ViewBag.Query ?? String.Empty;
string InputValue = ViewBag.Query ?? "";
string InitialMessage = ViewBag.Query == null ? "Search for a recipe" : $"Search results for '{ViewBag.Query}'";
}
<div>
<div id="search-page-header" class="mb-8 border-white border-b">
<h3 id="query-message"></h3>
<h3 id="query-message">@InitialMessage</h3>
<input value="@InputValue" class="text-black" id="new-search-query" type="text" />
<button id="execute-search">Search</button>
</div>
@@ -18,28 +19,4 @@
}
</div>
</div>
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", () => {
// dom elements
const query = window.location.search?.split("=")[1];
const message = document.getElementById("query-message");
const textField = document.getElementById("new-search-query");
const searchButton = document.getElementById("execute-search");
const searchResultsContainer = document.getElementById("search-results");
console.log(query);
// set initial message value
message.innerHTML = query ? `Viewing ${@Model.ToArray().Length} results for: ${query}` : "Enter a new search term below:";
// handle search updates on client side
searchButton.onclick = () => {
// const newResult = await fetch(`/api/recipe/search?q=${textField.value}`);
// console.log(newResult);
// const newResultJson = await newResult.json();
window.location.search = `?q=${textField.value}`;
}
});
</script>
<script href="~/js/searchRecipes.js"></script>

View File

@@ -1,4 +1,4 @@
export async function getAllIngredients () {
async function getAllIngredients () {
const ingredients = await fetch('/api/ingredients');
return await ingredients.json();
}

View File

@@ -0,0 +1,52 @@
import { isRecipeArray, type Recipe } from "./types";
function setSearchResultMessage(model: Recipe[]): void {
const message = document.getElementById("query-message");
message.innerHTML = `Viewing ${model.length} results for: ${window.location.search.split("=")[1]}`;
}
async function handleSearch(textField?: HTMLInputElement) {
if (!textField) {
console.log("NO TEXT FIELD!");
return;
}
const newResult = await fetch(`/api/recipe/search?q=${textField.value}`);
const newResultJson = await newResult.json();
const searchResultsContainer = document.getElementById("search-results");
if (isRecipeArray(newResultJson)) {
let updatedHTML = "";
for (const recipe of newResultJson) {
updatedHTML += `
<p>${recipe.Name}</p>
<p>${recipe.ShortDescription}</p>
`;
}
console.log(updatedHTML);
setSearchResultMessage(newResultJson);
searchResultsContainer.innerHTML = updatedHTML;
}
}
document.addEventListener('DOMContentLoaded', () => {
console.log("Loaded DOM");
// dom elements
const searchButton = document.getElementById("execute-search");
const inputNodes = document.querySelectorAll("input");
let textField: HTMLInputElement | null = null;
inputNodes.forEach(node => {
if (node.id == "new-search-query") {
textField = node;
console.log("Found text field");
}
});
// handle search updates on client side
searchButton.onclick = async () => handleSearch(textField);
});

View File

@@ -0,0 +1,2 @@
export * from "./models";
export * from "./validators";

View File

@@ -0,0 +1,25 @@
export type Recipe = {
RecipeId: number,
Name: string,
ShortDescription: string,
S3Url?: string,
Author?: string,
RecipeText?: string,
MainImageUrl?: string,
}
export type Ingredient = {
IngredientId: number,
Name: string,
Description?: string
}
export type RecipeIngredient = {
RecipeIngredientId: number,
RecipeId: number,
IngredientId: number,
Amount?: number,
Unit?: string,
}
export type IngredientWithDetails = Ingredient & Pick<RecipeIngredient, 'Amount' | 'Unit'>

View File

@@ -0,0 +1,37 @@
import type { Ingredient, Recipe } from "./models";
export function isRecipe(input: unknown): input is Recipe {
return (
typeof input == 'object'
&& input != null
&& 'RecipeId' in input
&& 'Name' in input
&& 'ShortDescription' in input
&& (input as Recipe).RecipeId != undefined
&& (input as Recipe).Name != undefined
&& (input as Recipe).ShortDescription != undefined
);
}
export function isArrayOfType<T>(input: unknown, isType: (input: unknown) => input is T): input is T[] {
return Array.isArray(input) && input.every(isType);
}
export function isRecipeArray(input: unknown): input is Recipe[] {
return isArrayOfType(input, isRecipe);
}
export function isIngredient(input: unknown): input is Ingredient {
return (
typeof input == 'object'
&& input != null
&& 'IngredientId' in input
&& 'Name' in input
&& (input as Ingredient).IngredientId != undefined
&& (input as Ingredient).Name != undefined
);
}
export function isIngredientArray(input: unknown): input is Ingredient[] {
return isArrayOfType(input, isIngredient);
}