Make UI better (WIP)
This commit is contained in:
parent
e26d4879e8
commit
898d5c5af3
4 changed files with 317 additions and 26 deletions
|
|
@ -6,6 +6,89 @@
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<link rel="icon" href={favicon} />
|
<link rel="icon" href={favicon} />
|
||||||
|
<title>City Portal</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
{@render children()}
|
<div class="app">
|
||||||
|
<header class="header">
|
||||||
|
<div class="brand">
|
||||||
|
<img src={favicon} alt="logo" class="logo" />
|
||||||
|
<span>City Portal</span>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="content">
|
||||||
|
{@render children()}
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* Reset + base */
|
||||||
|
.app {
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background: #ffffff;
|
||||||
|
color: #111827;
|
||||||
|
font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Header */
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 14px 24px;
|
||||||
|
border-bottom: 1px solid #e5e7eb;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.brand {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Content */
|
||||||
|
.content {
|
||||||
|
flex: 1;
|
||||||
|
padding: 24px;
|
||||||
|
max-width: 1000px;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fix ugly default link colors */
|
||||||
|
a {
|
||||||
|
color: #2563eb;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:visited {
|
||||||
|
color: #2563eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Optional: make buttons consistent if you use them */
|
||||||
|
button {
|
||||||
|
background: #2563eb;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background: #1d4ed8;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -1,7 +1,66 @@
|
||||||
<h1>Welcome to the City Portal!</h1>
|
<script lang="ts">
|
||||||
|
// no logic needed yet
|
||||||
|
</script>
|
||||||
|
|
||||||
<ul>
|
<main class="container">
|
||||||
<li><a href="datasets/">List of datasets</a></li>
|
<h1>City Portal</h1>
|
||||||
<li><a href="about/">Read about the project!</a></li>
|
<p class="subtitle">Access datasets, learn about the project, or explore the API.</p>
|
||||||
<li><a href="http://localhost:3000/graphiql">Directly interface with the backend</a></li>
|
|
||||||
</ul>
|
<nav>
|
||||||
|
<ul class="links">
|
||||||
|
<li>
|
||||||
|
<a href="/datasets">📊 Datasets</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/about">ℹ️ About the project</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="http://localhost:3000/graphiql" target="_blank" rel="noopener">
|
||||||
|
🧪 GraphiQL interface
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.container {
|
||||||
|
max-width: 700px;
|
||||||
|
margin: 4rem auto;
|
||||||
|
padding: 0 1rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
color: #94a3b8;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.links {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.links a {
|
||||||
|
display: block;
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
background: #1e293b;
|
||||||
|
color: #e2e8f0;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: transform 0.15s ease, background 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.links a:hover {
|
||||||
|
background: #334155;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -1,26 +1,122 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { PageProps } from "./$types";
|
import type { PageProps } from "./$types";
|
||||||
|
import { goto } from "$app/navigation";
|
||||||
|
|
||||||
let { data }: PageProps = $props();
|
let { data }: PageProps = $props();
|
||||||
|
|
||||||
|
function getIcon(name: string) {
|
||||||
|
switch (name) {
|
||||||
|
case "Weather":
|
||||||
|
return "☀️";
|
||||||
|
case "Transport":
|
||||||
|
return "🚌";
|
||||||
|
case "Events":
|
||||||
|
return "🎟️";
|
||||||
|
case "News":
|
||||||
|
return "📰";
|
||||||
|
case "Shops":
|
||||||
|
return "🏪";
|
||||||
|
case "Alerts":
|
||||||
|
return "⚠️";
|
||||||
|
case "Sports":
|
||||||
|
return "⚽";
|
||||||
|
default:
|
||||||
|
return "📊";
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>List of datasets</title>
|
<title>City Dashboard</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
{#if data.datasets.length}
|
{#if data.datasets.length}
|
||||||
{#each data.datasets as dataset}
|
<div class="grid">
|
||||||
<nav>
|
{#each data.datasets as dataset}
|
||||||
<ul>
|
<div class="card">
|
||||||
<li>
|
<button
|
||||||
<a href="datasets/{dataset.id}"><strong>{dataset.name}</strong></a><br
|
class="dataset-btn"
|
||||||
/>
|
on:click={() => goto(`/datasets/${dataset.id}`)}
|
||||||
|
>
|
||||||
|
<div class="icon">{getIcon(dataset.name)}</div>
|
||||||
|
<div class="name">{dataset.name}</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="desc">
|
||||||
{dataset.description}
|
{dataset.description}
|
||||||
</li>
|
</div>
|
||||||
</ul>
|
</div>
|
||||||
</nav>
|
{/each}
|
||||||
{/each}
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<h1>No datasets yet!</h1>
|
<h1>No datasets yet!</h1>
|
||||||
<p>Try adding a dataset through the backend.</p>
|
<p>Try adding a dataset through the backend.</p>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
gap: 18px;
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.grid {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 500px) {
|
||||||
|
.grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dataset-btn {
|
||||||
|
width: 100%;
|
||||||
|
background: #071a33;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 26px 16px;
|
||||||
|
border-radius: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
transition: background 0.2s, transform 0.1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dataset-btn:hover {
|
||||||
|
background: #0b2a52;
|
||||||
|
transform: translateY(-3px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dataset-btn:active {
|
||||||
|
transform: translateY(0px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
font-size: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.desc {
|
||||||
|
margin-top: 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #6b7280;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -3,14 +3,67 @@
|
||||||
|
|
||||||
let { data }: PageProps = $props();
|
let { data }: PageProps = $props();
|
||||||
|
|
||||||
// svelte-ignore state_referenced_locally
|
const recordData = JSON.parse(data.record.data);
|
||||||
const recordData = JSON.parse(data.record.data);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<h1>Data for record id: {data.record.id}</h1>
|
<svelte:head>
|
||||||
|
<title>Record {data.record.id}</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
<ul>
|
<div class="container">
|
||||||
{#each Object.entries(recordData) as [key, value]}
|
<h1 class="title">Record #{data.record.id}</h1>
|
||||||
<li>{key}: {value}</li>
|
|
||||||
{/each}
|
<div class="grid">
|
||||||
</ul>
|
{#each Object.entries(recordData) as [key, value]}
|
||||||
|
<div class="card">
|
||||||
|
<div class="key">{key}</div>
|
||||||
|
<div class="value">
|
||||||
|
{Array.isArray(value) ? value.join(", ") : value}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.container {
|
||||||
|
max-width: 900px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 24px;
|
||||||
|
font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background: #f9fafb;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.key {
|
||||||
|
font-size: 12px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: #6b7280;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #111827;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue