am i finished?
This commit is contained in:
@@ -1,16 +1,136 @@
|
||||
import { Component, For } from "solid-js";
|
||||
import { Component, For, createSignal } from "solid-js";
|
||||
import { useSearchImageContext } from "@contexts/SearchImageContext";
|
||||
import { ListCard } from "@components/list-card";
|
||||
import { Button } from "@kobalte/core/button";
|
||||
import { Dialog } from "@kobalte/core/dialog";
|
||||
import { createList } from "../../network";
|
||||
|
||||
export const Categories: Component = () => {
|
||||
const { lists } = useSearchImageContext();
|
||||
const { lists, onRefetchImages } = useSearchImageContext();
|
||||
|
||||
return (
|
||||
<div class="rounded-xl bg-white p-4 flex flex-col gap-2">
|
||||
<h2 class="text-xl font-bold">Generated Lists</h2>
|
||||
<div class="w-full grid grid-cols-3 auto-rows-[minmax(100px,1fr)] gap-4">
|
||||
<For each={lists()}>{(list) => <ListCard list={list} />}</For>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
const [title, setTitle] = createSignal("");
|
||||
const [description, setDescription] = createSignal("");
|
||||
|
||||
const [isCreating, setIsCreating] = createSignal(false);
|
||||
const [showForm, setShowForm] = createSignal(false);
|
||||
|
||||
const handleCreateList = async () => {
|
||||
if (description().trim().length === 0 || title().trim().length === 0)
|
||||
return;
|
||||
|
||||
setIsCreating(true);
|
||||
try {
|
||||
await createList(title().trim(), description().trim());
|
||||
setTitle("");
|
||||
setDescription("");
|
||||
setShowForm(false);
|
||||
onRefetchImages(); // Refresh the lists
|
||||
} catch (error) {
|
||||
console.error("Failed to create list:", error);
|
||||
} finally {
|
||||
setIsCreating(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div class="rounded-xl bg-white p-4 flex flex-col gap-2">
|
||||
<h2 class="text-xl font-bold">Generated Lists</h2>
|
||||
<div class="w-full grid grid-cols-3 auto-rows-[minmax(100px,1fr)] gap-4">
|
||||
<For each={lists()}>{(list) => <ListCard list={list} />}</For>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<Button
|
||||
class="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition-colors font-medium shadow-sm hover:shadow-md"
|
||||
onClick={() => setShowForm(true)}
|
||||
>
|
||||
+ Create List
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Dialog open={showForm()} onOpenChange={setShowForm}>
|
||||
<Dialog.Portal>
|
||||
<Dialog.Overlay class="fixed inset-0 bg-black/50 z-50" />
|
||||
<div class="fixed inset-0 z-50 flex items-center justify-center p-4">
|
||||
<Dialog.Content class="bg-white rounded-lg shadow-xl max-w-md w-full max-h-[90vh] overflow-y-auto">
|
||||
<div class="p-6">
|
||||
<Dialog.Title class="text-xl font-bold text-neutral-900 mb-4">
|
||||
Create New List
|
||||
</Dialog.Title>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label
|
||||
for="list-title"
|
||||
class="block text-sm font-medium text-neutral-700 mb-2"
|
||||
>
|
||||
List Title
|
||||
</label>
|
||||
<input
|
||||
id="list-title"
|
||||
type="text"
|
||||
value={title()}
|
||||
onInput={(e) =>
|
||||
setTitle(e.target.value)
|
||||
}
|
||||
placeholder="Enter a title for your list"
|
||||
class="w-full p-3 border border-neutral-300 rounded-lg focus:ring-2 focus:ring-indigo-600 focus:border-transparent transition-colors"
|
||||
disabled={isCreating()}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label
|
||||
for="list-description"
|
||||
class="block text-sm font-medium text-neutral-700 mb-2"
|
||||
>
|
||||
List Description
|
||||
</label>
|
||||
<textarea
|
||||
id="list-description"
|
||||
value={description()}
|
||||
onInput={(e) =>
|
||||
setDescription(e.target.value)
|
||||
}
|
||||
placeholder="Describe what kind of list you want to create (e.g., 'A list of my favorite recipes' or 'Photos from my vacation')"
|
||||
class="w-full p-3 border border-neutral-300 rounded-lg resize-none focus:ring-2 focus:ring-indigo-600 focus:border-transparent transition-colors"
|
||||
rows="4"
|
||||
disabled={isCreating()}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-3 mt-6">
|
||||
<Button
|
||||
class="flex-1 px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition-colors disabled:opacity-50 font-medium shadow-sm hover:shadow-md"
|
||||
onClick={handleCreateList}
|
||||
disabled={
|
||||
isCreating() ||
|
||||
!title().trim() ||
|
||||
!description().trim()
|
||||
}
|
||||
>
|
||||
{isCreating()
|
||||
? "Creating..."
|
||||
: "Create List"}
|
||||
</Button>
|
||||
<Button
|
||||
class="px-4 py-2 bg-neutral-300 text-neutral-700 rounded-lg hover:bg-neutral-400 transition-colors font-medium"
|
||||
onClick={() => {
|
||||
setShowForm(false);
|
||||
setTitle("");
|
||||
setDescription("");
|
||||
}}
|
||||
disabled={isCreating()}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog.Content>
|
||||
</div>
|
||||
</Dialog.Portal>
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,7 +3,6 @@ import { useSearchImageContext } from "@contexts/SearchImageContext";
|
||||
import { useParams } from "@solidjs/router";
|
||||
import { For, type Component } from "solid-js";
|
||||
import SolidjsMarkdown from "solidjs-markdown";
|
||||
import { List } from "../list";
|
||||
import { ListCard } from "@components/list-card";
|
||||
|
||||
export const ImagePage: Component = () => {
|
||||
|
||||
@@ -1,43 +1,107 @@
|
||||
import { ImageComponent } from "@components/image";
|
||||
import { useSearchImageContext } from "@contexts/SearchImageContext";
|
||||
import { useParams } from "@solidjs/router";
|
||||
import { Component, For, Show } from "solid-js";
|
||||
import { base } from "../../network";
|
||||
|
||||
export const List: Component = () => {
|
||||
const { listId } = useParams();
|
||||
const { listId } = useParams();
|
||||
|
||||
const { lists } = useSearchImageContext();
|
||||
const { lists } = useSearchImageContext();
|
||||
|
||||
const list = () => lists().find((l) => l.ID === listId);
|
||||
const list = () => lists().find((l) => l.ID === listId);
|
||||
|
||||
return (
|
||||
<Show when={list()} fallback="List could not be found">
|
||||
{(l) => (
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Image</th>
|
||||
<For each={l().Schema.SchemaItems}>
|
||||
{(item) => <th>{item.Item}</th>}
|
||||
</For>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<For each={l().Images}>
|
||||
{(image) => (
|
||||
<tr>
|
||||
<td>
|
||||
<ImageComponent ID={image.ImageID} />
|
||||
</td>
|
||||
<For each={image.Items}>
|
||||
{(item) => <td>{item.Value}</td>}
|
||||
</For>
|
||||
</tr>
|
||||
)}
|
||||
</For>
|
||||
</tbody>
|
||||
</table>
|
||||
)}
|
||||
</Show>
|
||||
);
|
||||
return (
|
||||
<Show when={list()} fallback="List could not be found">
|
||||
{(l) => (
|
||||
<div class="w-full h-full bg-white rounded-lg shadow-sm border border-neutral-200 overflow-hidden">
|
||||
<div class="overflow-x-auto overflow-y-auto h-full">
|
||||
<table class="w-full min-w-full">
|
||||
<thead class="bg-neutral-50 border-b border-neutral-200 sticky top-0 z-10">
|
||||
<tr>
|
||||
<th class="px-6 py-4 text-left text-sm font-semibold text-neutral-900 border-r border-neutral-200 min-w-40">
|
||||
Image
|
||||
</th>
|
||||
<For each={l().Schema.SchemaItems}>
|
||||
{(item, index) => (
|
||||
<th
|
||||
class={`px-6 py-4 text-left text-sm font-semibold text-neutral-900 min-w-32 ${
|
||||
index() <
|
||||
l().Schema.SchemaItems
|
||||
.length -
|
||||
1
|
||||
? "border-r border-neutral-200"
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
{item.Item}
|
||||
</th>
|
||||
)}
|
||||
</For>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-neutral-200">
|
||||
<For each={l().Images}>
|
||||
{(image, rowIndex) => (
|
||||
<tr
|
||||
class={`hover:bg-neutral-50 transition-colors ${
|
||||
rowIndex() % 2 === 0
|
||||
? "bg-white"
|
||||
: "bg-neutral-25"
|
||||
}`}
|
||||
>
|
||||
<td class="px-6 py-4 border-r border-neutral-200">
|
||||
<div class="w-32 h-24 overflow-hidden rounded-lg">
|
||||
<a
|
||||
href={`/image/${image.ImageID}`}
|
||||
class="w-full h-full flex justify-center"
|
||||
>
|
||||
<img
|
||||
class="w-full h-full object-cover rounded-lg"
|
||||
src={`${base}/images/${image.ImageID}`}
|
||||
alt="List item"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
<For each={image.Items}>
|
||||
{(item, colIndex) => (
|
||||
<td
|
||||
class={`px-6 py-4 text-sm text-neutral-700 ${
|
||||
colIndex() <
|
||||
image.Items.length -
|
||||
1
|
||||
? "border-r border-neutral-200"
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
class="max-w-xs truncate"
|
||||
title={item.Value}
|
||||
>
|
||||
{item.Value}
|
||||
</div>
|
||||
</td>
|
||||
)}
|
||||
</For>
|
||||
</tr>
|
||||
)}
|
||||
</For>
|
||||
</tbody>
|
||||
</table>
|
||||
<Show when={l().Images.length === 0}>
|
||||
<div class="px-6 py-12 text-center text-neutral-500">
|
||||
<p class="text-lg">
|
||||
No images in this list yet
|
||||
</p>
|
||||
<p class="text-sm mt-1">
|
||||
Images will appear here once added to the
|
||||
list
|
||||
</p>
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Show>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { isTokenValid } from "@components/protected-route";
|
||||
import { Button } from "@kobalte/core/button";
|
||||
import { TextField } from "@kobalte/core/text-field";
|
||||
import { postCode, postDemoLogin, postLogin } from "@network/index";
|
||||
import { postCode, postLogin } from "@network/index";
|
||||
import { Navigate } from "@solidjs/router";
|
||||
import { type Component, Show, createSignal } from "solid-js";
|
||||
|
||||
@@ -18,16 +18,6 @@ export const Login: Component = () => {
|
||||
throw new Error("bruh, no email");
|
||||
}
|
||||
|
||||
if (email.toString() === "demo@email.com") {
|
||||
const { access, refresh } = await postDemoLogin();
|
||||
|
||||
localStorage.setItem("access", access);
|
||||
localStorage.setItem("refresh", refresh);
|
||||
|
||||
window.location.href = "/";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!submitted()) {
|
||||
await postLogin(email.toString());
|
||||
setSubmitted(true);
|
||||
|
||||
Reference in New Issue
Block a user