Remove ionic, create custom layout

This commit is contained in:
Niklas Korz 2023-12-09 14:00:22 +01:00
parent b8bee291b3
commit 681440eb4d
23 changed files with 627 additions and 635 deletions

View file

@ -1,8 +1,8 @@
import { CapacitorConfig } from '@capacitor/cli'; import { CapacitorConfig } from '@capacitor/cli';
const appId = 'app.ionic.io'; const appId = 'dev.korz.musclecat';
const appName = 'app'; const appName = 'Musclecat';
const server = process.argv.includes('-hmr') ? { const server = process.argv.includes('-hmr') ? {
'url': 'http://192.168.178.25:5173', // always have http:// in url 'url': 'http://192.168.178.25:5173', // always have http:// in url
'cleartext': true 'cleartext': true

100
app/package-lock.json generated
View file

@ -9,9 +9,6 @@
"version": "0.0.1", "version": "0.0.1",
"dependencies": { "dependencies": {
"@capacitor/core": "^5.5.1", "@capacitor/core": "^5.5.1",
"@ionic/core": "^7.2.1",
"ionic-svelte": "^0.5.82",
"ionicons": "^7.2.1",
"pocketbase": "^0.19.0" "pocketbase": "^0.19.0"
}, },
"devDependencies": { "devDependencies": {
@ -561,36 +558,6 @@
"node": ">=16.0.0" "node": ">=16.0.0"
} }
}, },
"node_modules/@ionic/core": {
"version": "7.2.1",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-7.2.1.tgz",
"integrity": "sha512-7I3OIGHIhGCXxswphP3vfuyCCjXysAUg4cfKGB2QQ5e073DPsZkMSpVam19nAIjEDyfK2pQZC+SgSkZvvCc+2A==",
"dependencies": {
"@stencil/core": "^3.4.0",
"ionicons": "7.1.0",
"tslib": "^2.1.0"
}
},
"node_modules/@ionic/core/node_modules/ionicons": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/ionicons/-/ionicons-7.1.0.tgz",
"integrity": "sha512-iE4GuEdEHARJpp0sWL7WJZCzNCf5VxpNRhAjW0fLnZPnNL5qZOJUcfup2Z2Ty7Jk8Q5hacrHfGEB1lCwOdXqGg==",
"dependencies": {
"@stencil/core": "^2.18.0"
}
},
"node_modules/@ionic/core/node_modules/ionicons/node_modules/@stencil/core": {
"version": "2.22.3",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.22.3.tgz",
"integrity": "sha512-kmVA0M/HojwsfkeHsifvHVIYe4l5tin7J5+DLgtl8h6WWfiMClND5K3ifCXXI2ETDNKiEk21p6jql3Fx9o2rng==",
"bin": {
"stencil": "bin/stencil"
},
"engines": {
"node": ">=12.10.0",
"npm": ">=6.0.0"
}
},
"node_modules/@ionic/utils-array": { "node_modules/@ionic/utils-array": {
"version": "2.1.6", "version": "2.1.6",
"resolved": "https://registry.npmjs.org/@ionic/utils-array/-/utils-array-2.1.6.tgz", "resolved": "https://registry.npmjs.org/@ionic/utils-array/-/utils-array-2.1.6.tgz",
@ -823,18 +790,6 @@
"integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==",
"dev": true "dev": true
}, },
"node_modules/@stencil/core": {
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-3.4.2.tgz",
"integrity": "sha512-FAUhUVaakCy29nU2GwO/HQBRV1ihPRvncz3PUc8oR+UJLAxGabTmP8PLY7wvHfbw+Cvi4VXfJFTBvdfDu6iKPQ==",
"bin": {
"stencil": "bin/stencil"
},
"engines": {
"node": ">=14.10.0",
"npm": ">=6.0.0"
}
},
"node_modules/@sveltejs/adapter-static": { "node_modules/@sveltejs/adapter-static": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-2.0.3.tgz", "resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-2.0.3.tgz",
@ -2535,35 +2490,6 @@
"node": "^12.13.0 || ^14.15.0 || >=16.0.0" "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
} }
}, },
"node_modules/ionic-svelte": {
"version": "0.5.82",
"resolved": "https://registry.npmjs.org/ionic-svelte/-/ionic-svelte-0.5.82.tgz",
"integrity": "sha512-8VUsHXervP+6uFZbRxedrUml757RntbadBdtgUr+3aXJbVQSDD3YaAaj2bv1WI02MyO6WqSW+uVk1+wb4s1urg==",
"dependencies": {
"@ionic/core": "^7.2.1",
"swiper": "^9.2.4"
}
},
"node_modules/ionicons": {
"version": "7.2.1",
"resolved": "https://registry.npmjs.org/ionicons/-/ionicons-7.2.1.tgz",
"integrity": "sha512-2pvCM7DGVEtbbj48PJzQrCADCQrqjU1nUYX9l9PyEWz3ZfdnLdAouqwPxLdl8tbaF9cE7OZRSlyQD7oLOLnGoQ==",
"dependencies": {
"@stencil/core": "^4.0.3"
}
},
"node_modules/ionicons/node_modules/@stencil/core": {
"version": "4.8.1",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.8.1.tgz",
"integrity": "sha512-KG1H10j24rlyxIqOI4CG8/h9T7ObTv7giW2H3u1qXV4KKrLykDOpMcLzpqNXqL2Fki3s1QvHyl/oaRvi5waWVw==",
"bin": {
"stencil": "bin/stencil"
},
"engines": {
"node": ">=16.0.0",
"npm": ">=7.10.0"
}
},
"node_modules/is-binary-path": { "node_modules/is-binary-path": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
@ -3841,11 +3767,6 @@
"node": ">= 10.x" "node": ">= 10.x"
} }
}, },
"node_modules/ssr-window": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/ssr-window/-/ssr-window-4.0.2.tgz",
"integrity": "sha512-ISv/Ch+ig7SOtw7G2+qkwfVASzazUnvlDTwypdLoPoySv+6MqlOV10VwPSE6EWkGjhW50lUmghPmpYZXMu/+AQ=="
},
"node_modules/stackback": { "node_modules/stackback": {
"version": "0.0.2", "version": "0.0.2",
"resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
@ -4100,27 +4021,6 @@
"node": ">=12" "node": ">=12"
} }
}, },
"node_modules/swiper": {
"version": "9.4.1",
"resolved": "https://registry.npmjs.org/swiper/-/swiper-9.4.1.tgz",
"integrity": "sha512-1nT2T8EzUpZ0FagEqaN/YAhRj33F2x/lN6cyB0/xoYJDMf8KwTFT3hMOeoB8Tg4o3+P/CKqskP+WX0Df046fqA==",
"funding": [
{
"type": "patreon",
"url": "https://www.patreon.com/swiperjs"
},
{
"type": "open_collective",
"url": "http://opencollective.com/swiper"
}
],
"dependencies": {
"ssr-window": "^4.0.2"
},
"engines": {
"node": ">= 4.7.0"
}
},
"node_modules/tar": { "node_modules/tar": {
"version": "6.2.0", "version": "6.2.0",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz",

View file

@ -37,9 +37,6 @@
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@capacitor/core": "^5.5.1", "@capacitor/core": "^5.5.1",
"@ionic/core": "^7.2.1",
"ionic-svelte": "^0.5.82",
"ionicons": "^7.2.1",
"pocketbase": "^0.19.0" "pocketbase": "^0.19.0"
} }
} }

View file

@ -4,6 +4,10 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" /> <link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Segoe+UI:wght@400;600&display=swap"
/>
%sveltekit.head% %sveltekit.head%
</head> </head>
<body data-sveltekit-preload-data="hover"> <body data-sveltekit-preload-data="hover">

View file

@ -7,6 +7,7 @@ import type { RecordService } from 'pocketbase'
export enum Collections { export enum Collections {
Completions = "completions", Completions = "completions",
Icons = "icons",
Lists = "lists", Lists = "lists",
Tasks = "tasks", Tasks = "tasks",
Users = "users", Users = "users",
@ -41,6 +42,11 @@ export type CompletionsRecord = {
user?: RecordIdString user?: RecordIdString
} }
export type IconsRecord = {
image?: string
name?: string
}
export type ListsRecord = { export type ListsRecord = {
name?: string name?: string
parent?: RecordIdString parent?: RecordIdString
@ -49,6 +55,7 @@ export type ListsRecord = {
export type TasksRecord = { export type TasksRecord = {
cooldown?: number cooldown?: number
description?: HTMLString description?: HTMLString
icon?: RecordIdString
list: RecordIdString list: RecordIdString
name?: string name?: string
schedule?: IsoDateString schedule?: IsoDateString
@ -61,6 +68,7 @@ export type UsersRecord = {
// Response types include system fields and match responses from the PocketBase API // Response types include system fields and match responses from the PocketBase API
export type CompletionsResponse<Texpand = unknown> = Required<CompletionsRecord> & BaseSystemFields<Texpand> export type CompletionsResponse<Texpand = unknown> = Required<CompletionsRecord> & BaseSystemFields<Texpand>
export type IconsResponse<Texpand = unknown> = Required<IconsRecord> & BaseSystemFields<Texpand>
export type ListsResponse<Texpand = unknown> = Required<ListsRecord> & BaseSystemFields<Texpand> export type ListsResponse<Texpand = unknown> = Required<ListsRecord> & BaseSystemFields<Texpand>
export type TasksResponse<Texpand = unknown> = Required<TasksRecord> & BaseSystemFields<Texpand> export type TasksResponse<Texpand = unknown> = Required<TasksRecord> & BaseSystemFields<Texpand>
export type UsersResponse<Texpand = unknown> = Required<UsersRecord> & AuthSystemFields<Texpand> export type UsersResponse<Texpand = unknown> = Required<UsersRecord> & AuthSystemFields<Texpand>
@ -69,6 +77,7 @@ export type UsersResponse<Texpand = unknown> = Required<UsersRecord> & AuthSyste
export type CollectionRecords = { export type CollectionRecords = {
completions: CompletionsRecord completions: CompletionsRecord
icons: IconsRecord
lists: ListsRecord lists: ListsRecord
tasks: TasksRecord tasks: TasksRecord
users: UsersRecord users: UsersRecord
@ -76,6 +85,7 @@ export type CollectionRecords = {
export type CollectionResponses = { export type CollectionResponses = {
completions: CompletionsResponse completions: CompletionsResponse
icons: IconsResponse
lists: ListsResponse lists: ListsResponse
tasks: TasksResponse tasks: TasksResponse
users: UsersResponse users: UsersResponse
@ -86,6 +96,7 @@ export type CollectionResponses = {
export type TypedPocketBase = PocketBase & { export type TypedPocketBase = PocketBase & {
collection(idOrName: 'completions'): RecordService<CompletionsResponse> collection(idOrName: 'completions'): RecordService<CompletionsResponse>
collection(idOrName: 'icons'): RecordService<IconsResponse>
collection(idOrName: 'lists'): RecordService<ListsResponse> collection(idOrName: 'lists'): RecordService<ListsResponse>
collection(idOrName: 'tasks'): RecordService<TasksResponse> collection(idOrName: 'tasks'): RecordService<TasksResponse>
collection(idOrName: 'users'): RecordService<UsersResponse> collection(idOrName: 'users'): RecordService<UsersResponse>

View file

@ -1,48 +1,46 @@
<script lang="ts"> <script lang="ts">
import { setupIonicBase } from 'ionic-svelte'; import { goto } from '$app/navigation';
import { pb } from '$lib/api';
/* Call Ionic's setup routine */ import '../theme/global.css';
setupIonicBase();
/* Import all components - or do partial loading - see below */
import 'ionic-svelte/components/all';
/* Theme variables */ /* Theme variables */
import '../theme/variables.css'; import '../theme/variables.css';
/* async function goToRandomTask() {
This part - import 'ionic-svelte/components/all'; - loads all components at once. const task = await pb
.collection('tasks')
.getFirstListItem('', { sort: '@random', fields: 'id' });
goto(`/tasks/${task.id}`);
}
This adds at least >800kb (uncompressed) to your bundle - 80 components (so do your math!!) let listsLoading = pb.collection("lists").getList(1, 50);
You can also choose to import each component you want to use separately and leave out others.
It is recommended to do this in this file, as you only need to do such once. But you are free
to do this elsewhere if you like to code-split differently.
Example: if you replace the line import 'ionic-svelte/components/all'; with the imports below, you will see the resulting bundle being much smaller
import 'ionic-svelte/components/ion-app';
import 'ionic-svelte/components/ion-card';
import 'ionic-svelte/components/ion-card-title';
import 'ionic-svelte/components/ion-card-subtitle';
import 'ionic-svelte/components/ion-card-header';
import 'ionic-svelte/components/ion-card-content';
import 'ionic-svelte/components/ion-chip';
import 'ionic-svelte/components/ion-button';
Click the ionic-svelte-components-all-import above to go to the full list of possible imports.
Please don't forget to import ion-app in this file when you decide to code-split:
import 'ionic-svelte/components/ion-app';
You can report issues here - https://github.com/Tommertom/svelte-ionic-npm/issues
Want to know what is happening more - follow me on Twitter - https://twitter.com/Tommertomm
Discord channel on Ionic server - https://discordapp.com/channels/520266681499779082/1049388501629681675
*/
</script> </script>
<ion-app> <div class="topbar">
<slot /> <div class="app-name">
</ion-app> <img width="30" height="30" alt="Musclecat logo" src="/logo.webp" />
<h1>Musceclat</h1>
</div>
<div class="search">
<input type="search" placeholder="Search tasks" />
<button on:click={goToRandomTask}>Zufällig</button>
</div>
</div>
<div class="container">
<div class="sidebar">
<div class="sidebar-heading">Lists</div>
{#await listsLoading}
<div class="list-name selected">Loading...</div>
{:then lists}
{#each lists.items as list}
<div class="list-name">{list.name}</div>
{/each}
{/await}
<div class="progress-bar-container">
<div class="progress-bar">70%</div>
</div>
</div>
<div class="content">
<slot />
</div>
</div>

View file

@ -1,43 +1,18 @@
<ion-card> <script lang="ts">
<ion-card-header> import type { PageData } from './$types';
<ion-card-subtitle>Great success!!</ion-card-subtitle>
<ion-card-title>Welcome to your app!</ion-card-title>
</ion-card-header>
<ion-card-content> export let data: PageData;
Thank you for using this starter. Click buttons below to open these guides (will open in new
window). Don't forget to open DevTools to see this app in mobile mode. Happy coding!!!
</ion-card-content>
<ion-item> let { tasks } = data;
<ion-label>Visit Ionic Showcase app with sourceviewer</ion-label> </script>
<ion-button href="https://ionicsvelte.firebaseapp.com/" target="_new" fill="outline" slot="end"
>View</ion-button
>
</ion-item>
<ion-item> <h2>Haushalt</h2>
<ion-label>Visit Ionic component docs</ion-label> <ul class="task-list">
<ion-button {#each tasks.items as task}
href="https://ionicframework.com/docs/components" <li>
target="_new" {task.name}
fill="outline" <span>{task.expand?.list.name}</span>
slot="end">View</ion-button </li>
> {/each}
</ion-item> </ul>
<ion-item> <button>Neue Aufgabe</button>
<ion-label>Visit Svelte Kit docs</ion-label>
<ion-button
href="https://kit.svelte.dev/docs/introduction"
target="_new"
fill="outline"
slot="end">View</ion-button
>
</ion-item>
<ion-item>
<ion-label>Visit Svelte docs</ion-label>
<ion-button href="https://svelte.dev/docs" target="_new" fill="outline" slot="end"
>View</ion-button
>
</ion-item>
</ion-card>

30
app/src/routes/+page.ts Normal file
View file

@ -0,0 +1,30 @@
import { pb } from '$lib/api';
import { ClientResponseError, type ListResult } from 'pocketbase';
import type { PageLoad } from './$types';
import { error } from '@sveltejs/kit';
import type { IconsResponse, ListsResponse, TasksResponse } from '$lib/pocketbase-types';
interface Expand {
icon?: IconsResponse;
list: ListsResponse;
}
export const load: PageLoad = async function () {
try {
const tasks: ListResult<TasksResponse<Expand>> = await pb.collection('tasks').getList(1, 50, {
expand: "icon,list"
});
/*const icon = task.expand?.icon;
const iconUrl =
icon &&
pb.files.getUrl(icon, icon.image, {
thumb: '100x100'
});*/
return { tasks };
} catch (ex) {
if (ex instanceof ClientResponseError) {
throw error(ex.status, ex.response.message);
}
throw ex;
}
};

View file

@ -0,0 +1,51 @@
<script lang="ts">
import type { ListsResponse } from '$lib/pocketbase-types';
import type { PageData } from './$types';
export let data: PageData;
const rootLists = data.lists.filter((l) => !l.parent);
const children = data.lists.reduce((acc, l) => {
if (acc.has(l.parent)) {
acc.get(l.parent)?.push(l);
} else {
acc.set(l.parent, [l]);
}
return acc;
}, new Map<string, ListsResponse[]>());
// TODO: Replace with navigation logic, such as using a Svelte store or a Router
const goToTasks = (taskId: string) => {
console.log(`Redirecting to tasks for list ${taskId}`);
};
</script>
<ion-header>
<ion-toolbar>
<ion-title>Task Lists</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
{#each rootLists as list}
<ion-item lines="full">
<ion-label>
<h2>{list.name}</h2>
<ion-list>
{#each children.get(list.id) as childList}
<ion-item lines="full">
<ion-label>
<h2>{childList.name}</h2>
</ion-label>
</ion-item>
{/each}
</ion-list>
</ion-label>
</ion-item>
{/each}
</ion-list>
</ion-content>
<style>
/* Additional styles if necessary */
</style>

View file

@ -0,0 +1,7 @@
import { pb } from '$lib/api';
import type { PageLoad } from './$types';
export const load: PageLoad = async function () {
const lists = await pb.collection('lists').getFullList();
return { lists };
};

View file

@ -1,72 +1,93 @@
<script lang="ts"> <script lang="ts">
import { pb } from "$lib/api"; import { pb } from '$lib/api';
let email = ''; let email = '';
let password = ''; let password = '';
const handleLogin = async (event: Event) => { const handleLogin = async (event: Event) => {
// Prevent the form from submitting normally
event.preventDefault(); event.preventDefault();
email = email.trim(); email = email.trim();
password = password.trim(); password = password.trim();
// Replace with your own login logic
if (email && password) { if (email && password) {
console.log('Login submitted:', email, password); const resp = await pb.collection('users').authWithPassword(email, password);
const resp = await pb.collection("users").authWithPassword(email, password); console.log('Logged in:', resp);
console.log('Logged in:', resp);
} }
}; };
// Event handlers for ionic-input
const handleEmailInput = (event: { target: HTMLIonInputElement }) => {
email = event.target.value as string;
};
const handlePasswordInput = (event: { target: HTMLIonInputElement }) => {
password = event.target.value as string;
};
</script> </script>
<ion-app> <form class="login-container" on:submit={handleLogin}>
<ion-header> <h1>Musceclat Login</h1>
<ion-toolbar> <div class="form-group">
<ion-title>Login</ion-title> <label for="email">E-Mail:</label>
</ion-toolbar> <input
</ion-header> type="email"
id="email"
<ion-content class="ion-padding"> name="email"
<form on:submit={handleLogin}> placeholder="Enter your email"
<ion-item> required
<ion-label position="stacked">Email</ion-label> bind:value={email}
<ion-input />
type="email" </div>
placeholder="Enter your email" <div class="form-group">
value={email} <label for="password">Password:</label>
on:ionInput={handleEmailInput} <input
required type="password"
> id="password"
</ion-input> name="password"
</ion-item> placeholder="Enter your password"
required
<ion-item> bind:value={password}
<ion-label position="stacked">Password</ion-label> />
<ion-input </div>
type="password" <button type="submit">Login</button>
placeholder="Enter your password" </form>
value={password}
on:ionInput={handlePasswordInput}
required
>
</ion-input>
</ion-item>
<ion-button type="submit" expand="block" class="ion-margin-top"> Login </ion-button>
</form>
</ion-content>
</ion-app>
<style> <style>
/* Additional styles if necessary */ .login-container {
background-color: #ffffff;
padding: 40px;
border-radius: 8px;
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2);
width: 100%;
max-width: 400px;
}
.login-container h1 {
font-size: 2em;
color: #202020;
margin-bottom: 20px;
text-align: center;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
color: #606060;
margin-bottom: 5px;
}
.form-group input {
width: 100%;
padding: 15px;
border: none;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
font-size: 16px;
}
button {
background-color: #0078d4;
color: white;
padding: 15px;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 16px;
width: 100%;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
transition: background-color 0.3s;
}
button:hover {
background-color: #005ea6;
}
</style> </style>

View file

@ -0,0 +1,9 @@
<script lang="ts">
import type { PageData } from './$types';
export let data: PageData;
</script>
<h2>Task: {data.task.name}</h2>
<img width="100" height="100" alt="icon" src={data.iconUrl} />
{@html data.task.description}

View file

@ -0,0 +1,27 @@
import { pb } from '$lib/api';
import { ClientResponseError } from 'pocketbase';
import type { PageLoad } from './$types';
import { error } from '@sveltejs/kit';
import type { IconsResponse, TasksResponse } from '$lib/pocketbase-types';
interface Expand {
icon?: IconsResponse;
}
export const load: PageLoad = async function ({ params: { id } }) {
try {
const task: TasksResponse<Expand> = await pb.collection('tasks').getOne(id, { expand: 'icon' });
const icon = task.expand?.icon;
const iconUrl =
icon &&
pb.files.getUrl(icon, icon.image, {
thumb: '100x100'
});
return { task, iconUrl };
} catch (ex) {
if (ex instanceof ClientResponseError) {
throw error(ex.status, ex.response.message);
}
throw ex;
}
};

178
app/src/theme/global.css Normal file
View file

@ -0,0 +1,178 @@
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: "Segoe UI", sans-serif;
background-color: #f3f3f3;
color: #202020;
padding: 10px;
}
.container {
display: flex;
height: calc(100vh - 20px);
}
.sidebar {
flex: 0 0 300px;
background-color: #ffffff;
padding: 20px;
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2);
margin-right: 20px;
border-radius: 8px;
overflow: hidden;
}
.sidebar-heading {
font-size: 24px;
color: #202020;
margin-bottom: 20px;
font-weight: 600;
}
.list-name {
padding: 10px 0;
cursor: pointer;
border-radius: 4px;
transition: background-color 0.3s;
}
.list-name:hover {
background-color: #f0f0f0;
}
.progress-bar-container {
width: 100%;
background-color: #e1e1e1;
border-radius: 10px;
margin: 20px 0;
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2);
}
.progress-bar {
height: 20px;
background-color: #0078d4;
width: 70%;
/* Dynamic value here */
border-radius: 8px;
transition: width 0.5s ease-in-out;
display: flex;
align-items: center;
justify-content: center;
color: #ffffff;
font-size: 12px;
}
.topbar {
display: flex;
align-items: center;
margin-bottom: 20px;
}
.app-name {
display: flex;
align-items: center;
font-weight: 600;
font-size: 1.5em;
color: #202020;
margin-right: 20px;
}
h1 {
font-size: 1em;
}
.app-logo {
width: 36px;
height: 36px;
margin-right: 10px;
fill: currentColor;
}
.search {
display: flex;
flex: 1;
align-items: center;
}
.search input {
padding: 15px;
border: none;
border-radius: 8px;
font-size: 16px;
flex: 1;
margin-right: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.search button {
background-color: #0078d4;
border: none;
color: white;
padding: 15px;
font-size: 16px;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
cursor: pointer;
transition: background-color 0.3s;
}
.search button:hover {
background-color: #005ea6;
}
.content {
flex: 1;
padding: 20px;
background-color: #ffffff;
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2);
border-radius: 8px;
}
.content h2 {
font-size: 24px;
color: #202020;
margin-bottom: 20px;
font-weight: 600;
}
.task-list li {
list-style: none;
background-color: #f9f9f9;
padding: 15px;
margin: 10px 0;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
transition: background-color 0.3s;
}
.task-list li:hover {
background-color: #f3f3f3;
}
.task-list li span {
display: block;
color: #606060;
margin-top: 5px;
font-size: 14px;
font-weight: 400;
}
.content button {
background-color: #0078d4;
border: none;
color: white;
padding: 15px;
font-size: 16px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
cursor: pointer;
transition: background-color 0.3s;
}
.content button:hover {
background-color: #005ea6;
}

View file

@ -1,388 +0,0 @@
/* Ionic Variables and Theming. For more info, please see:
http://ionicframework.com/docs/theming/ */
/** Ionic CSS Variables **/
:root {
/** primary **/
--ion-color-primary: #3880ff;
--ion-color-primary-rgb: 56, 128, 255;
--ion-color-primary-contrast: #ffffff;
--ion-color-primary-contrast-rgb: 255, 255, 255;
--ion-color-primary-shade: #3171e0;
--ion-color-primary-tint: #4c8dff;
/** secondary **/
--ion-color-secondary: #3dc2ff;
--ion-color-secondary-rgb: 61, 194, 255;
--ion-color-secondary-contrast: #ffffff;
--ion-color-secondary-contrast-rgb: 255, 255, 255;
--ion-color-secondary-shade: #36abe0;
--ion-color-secondary-tint: #50c8ff;
/** tertiary **/
--ion-color-tertiary: #5260ff;
--ion-color-tertiary-rgb: 82, 96, 255;
--ion-color-tertiary-contrast: #ffffff;
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
--ion-color-tertiary-shade: #4854e0;
--ion-color-tertiary-tint: #6370ff;
/** success **/
--ion-color-success: #2dd36f;
--ion-color-success-rgb: 45, 211, 111;
--ion-color-success-contrast: #ffffff;
--ion-color-success-contrast-rgb: 255, 255, 255;
--ion-color-success-shade: #28ba62;
--ion-color-success-tint: #42d77d;
/** warning **/
--ion-color-warning: #ffc409;
--ion-color-warning-rgb: 255, 196, 9;
--ion-color-warning-contrast: #000000;
--ion-color-warning-contrast-rgb: 0, 0, 0;
--ion-color-warning-shade: #e0ac08;
--ion-color-warning-tint: #ffca22;
/** danger **/
--ion-color-danger: #eb445a;
--ion-color-danger-rgb: 235, 68, 90;
--ion-color-danger-contrast: #ffffff;
--ion-color-danger-contrast-rgb: 255, 255, 255;
--ion-color-danger-shade: #cf3c4f;
--ion-color-danger-tint: #ed576b;
/** dark **/
--ion-color-dark: #222428;
--ion-color-dark-rgb: 34, 36, 40;
--ion-color-dark-contrast: #ffffff;
--ion-color-dark-contrast-rgb: 255, 255, 255;
--ion-color-dark-shade: #1e2023;
--ion-color-dark-tint: #383a3e;
/** medium **/
--ion-color-medium: #92949c;
--ion-color-medium-rgb: 146, 148, 156;
--ion-color-medium-contrast: #ffffff;
--ion-color-medium-contrast-rgb: 255, 255, 255;
--ion-color-medium-shade: #808289;
--ion-color-medium-tint: #9d9fa6;
/** light **/
--ion-color-light: #f4f5f8;
--ion-color-light-rgb: 244, 245, 248;
--ion-color-light-contrast: #000000;
--ion-color-light-contrast-rgb: 0, 0, 0;
--ion-color-light-shade: #d7d8da;
--ion-color-light-tint: #f5f6f9;
}
@media (prefers-color-scheme: dark) {
/*
* Dark Colors
* -------------------------------------------
*/
body {
--ion-color-primary: #428cff;
--ion-color-primary-rgb: 66, 140, 255;
--ion-color-primary-contrast: #ffffff;
--ion-color-primary-contrast-rgb: 255, 255, 255;
--ion-color-primary-shade: #3a7be0;
--ion-color-primary-tint: #5598ff;
--ion-color-secondary: #50c8ff;
--ion-color-secondary-rgb: 80, 200, 255;
--ion-color-secondary-contrast: #ffffff;
--ion-color-secondary-contrast-rgb: 255, 255, 255;
--ion-color-secondary-shade: #46b0e0;
--ion-color-secondary-tint: #62ceff;
--ion-color-tertiary: #6a64ff;
--ion-color-tertiary-rgb: 106, 100, 255;
--ion-color-tertiary-contrast: #ffffff;
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
--ion-color-tertiary-shade: #5d58e0;
--ion-color-tertiary-tint: #7974ff;
--ion-color-success: #2fdf75;
--ion-color-success-rgb: 47, 223, 117;
--ion-color-success-contrast: #000000;
--ion-color-success-contrast-rgb: 0, 0, 0;
--ion-color-success-shade: #29c467;
--ion-color-success-tint: #44e283;
--ion-color-warning: #ffd534;
--ion-color-warning-rgb: 255, 213, 52;
--ion-color-warning-contrast: #000000;
--ion-color-warning-contrast-rgb: 0, 0, 0;
--ion-color-warning-shade: #e0bb2e;
--ion-color-warning-tint: #ffd948;
--ion-color-danger: #ff4961;
--ion-color-danger-rgb: 255, 73, 97;
--ion-color-danger-contrast: #ffffff;
--ion-color-danger-contrast-rgb: 255, 255, 255;
--ion-color-danger-shade: #e04055;
--ion-color-danger-tint: #ff5b71;
--ion-color-dark: #f4f5f8;
--ion-color-dark-rgb: 244, 245, 248;
--ion-color-dark-contrast: #000000;
--ion-color-dark-contrast-rgb: 0, 0, 0;
--ion-color-dark-shade: #d7d8da;
--ion-color-dark-tint: #f5f6f9;
--ion-color-medium: #989aa2;
--ion-color-medium-rgb: 152, 154, 162;
--ion-color-medium-contrast: #000000;
--ion-color-medium-contrast-rgb: 0, 0, 0;
--ion-color-medium-shade: #86888f;
--ion-color-medium-tint: #a2a4ab;
--ion-color-light: #222428;
--ion-color-light-rgb: 34, 36, 40;
--ion-color-light-contrast: #ffffff;
--ion-color-light-contrast-rgb: 255, 255, 255;
--ion-color-light-shade: #1e2023;
--ion-color-light-tint: #383a3e;
}
/*
* iOS Dark Theme
* -------------------------------------------
*/
.ios body {
--ion-background-color: #000000;
--ion-background-color-rgb: 0, 0, 0;
--ion-text-color: #ffffff;
--ion-text-color-rgb: 255, 255, 255;
--ion-color-step-50: #0d0d0d;
--ion-color-step-100: #1a1a1a;
--ion-color-step-150: #262626;
--ion-color-step-200: #333333;
--ion-color-step-250: #404040;
--ion-color-step-300: #4d4d4d;
--ion-color-step-350: #595959;
--ion-color-step-400: #666666;
--ion-color-step-450: #737373;
--ion-color-step-500: #808080;
--ion-color-step-550: #8c8c8c;
--ion-color-step-600: #999999;
--ion-color-step-650: #a6a6a6;
--ion-color-step-700: #b3b3b3;
--ion-color-step-750: #bfbfbf;
--ion-color-step-800: #cccccc;
--ion-color-step-850: #d9d9d9;
--ion-color-step-900: #e6e6e6;
--ion-color-step-950: #f2f2f2;
--ion-item-background: #000000;
--ion-card-background: #1c1c1d;
}
.ios ion-modal {
--ion-background-color: var(--ion-color-step-100);
--ion-toolbar-background: var(--ion-color-step-150);
--ion-toolbar-border-color: var(--ion-color-step-250);
}
/*
* Material Design Dark Theme
* -------------------------------------------
*/
.md body {
--ion-background-color: #121212;
--ion-background-color-rgb: 18, 18, 18;
--ion-text-color: #ffffff;
--ion-text-color-rgb: 255, 255, 255;
--ion-border-color: #222222;
--ion-color-step-50: #1e1e1e;
--ion-color-step-100: #2a2a2a;
--ion-color-step-150: #363636;
--ion-color-step-200: #414141;
--ion-color-step-250: #4d4d4d;
--ion-color-step-300: #595959;
--ion-color-step-350: #656565;
--ion-color-step-400: #717171;
--ion-color-step-450: #7d7d7d;
--ion-color-step-500: #898989;
--ion-color-step-550: #949494;
--ion-color-step-600: #a0a0a0;
--ion-color-step-650: #acacac;
--ion-color-step-700: #b8b8b8;
--ion-color-step-750: #c4c4c4;
--ion-color-step-800: #d0d0d0;
--ion-color-step-850: #dbdbdb;
--ion-color-step-900: #e7e7e7;
--ion-color-step-950: #f3f3f3;
--ion-item-background: #1e1e1e;
--ion-toolbar-background: #1f1f1f;
--ion-tab-bar-background: #1f1f1f;
--ion-card-background: #1e1e1e;
}
}
/*
* Classic class selector way - assign dark md or dark ios to force dark mode
* See https://ionicframework.com/docs/theming/dark-mode
*/
body.dark {
/* Dark mode variables go here */
/*
* Dark Colors
* -------------------------------------------
*/
--ion-color-primary: #428cff;
--ion-color-primary-rgb: 66, 140, 255;
--ion-color-primary-contrast: #ffffff;
--ion-color-primary-contrast-rgb: 255, 255, 255;
--ion-color-primary-shade: #3a7be0;
--ion-color-primary-tint: #5598ff;
--ion-color-secondary: #50c8ff;
--ion-color-secondary-rgb: 80, 200, 255;
--ion-color-secondary-contrast: #ffffff;
--ion-color-secondary-contrast-rgb: 255, 255, 255;
--ion-color-secondary-shade: #46b0e0;
--ion-color-secondary-tint: #62ceff;
--ion-color-tertiary: #6a64ff;
--ion-color-tertiary-rgb: 106, 100, 255;
--ion-color-tertiary-contrast: #ffffff;
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
--ion-color-tertiary-shade: #5d58e0;
--ion-color-tertiary-tint: #7974ff;
--ion-color-success: #2fdf75;
--ion-color-success-rgb: 47, 223, 117;
--ion-color-success-contrast: #000000;
--ion-color-success-contrast-rgb: 0, 0, 0;
--ion-color-success-shade: #29c467;
--ion-color-success-tint: #44e283;
--ion-color-warning: #ffd534;
--ion-color-warning-rgb: 255, 213, 52;
--ion-color-warning-contrast: #000000;
--ion-color-warning-contrast-rgb: 0, 0, 0;
--ion-color-warning-shade: #e0bb2e;
--ion-color-warning-tint: #ffd948;
--ion-color-danger: #ff4961;
--ion-color-danger-rgb: 255, 73, 97;
--ion-color-danger-contrast: #ffffff;
--ion-color-danger-contrast-rgb: 255, 255, 255;
--ion-color-danger-shade: #e04055;
--ion-color-danger-tint: #ff5b71;
--ion-color-dark: #f4f5f8;
--ion-color-dark-rgb: 244, 245, 248;
--ion-color-dark-contrast: #000000;
--ion-color-dark-contrast-rgb: 0, 0, 0;
--ion-color-dark-shade: #d7d8da;
--ion-color-dark-tint: #f5f6f9;
--ion-color-medium: #989aa2;
--ion-color-medium-rgb: 152, 154, 162;
--ion-color-medium-contrast: #000000;
--ion-color-medium-contrast-rgb: 0, 0, 0;
--ion-color-medium-shade: #86888f;
--ion-color-medium-tint: #a2a4ab;
--ion-color-light: #222428;
--ion-color-light-rgb: 34, 36, 40;
--ion-color-light-contrast: #ffffff;
--ion-color-light-contrast-rgb: 255, 255, 255;
--ion-color-light-shade: #1e2023;
--ion-color-light-tint: #383a3e;
}
body.dark.ios {
--ion-background-color: #000000;
--ion-background-color-rgb: 0, 0, 0;
--ion-text-color: #ffffff;
--ion-text-color-rgb: 255, 255, 255;
--ion-color-step-50: #0d0d0d;
--ion-color-step-100: #1a1a1a;
--ion-color-step-150: #262626;
--ion-color-step-200: #333333;
--ion-color-step-250: #404040;
--ion-color-step-300: #4d4d4d;
--ion-color-step-350: #595959;
--ion-color-step-400: #666666;
--ion-color-step-450: #737373;
--ion-color-step-500: #808080;
--ion-color-step-550: #8c8c8c;
--ion-color-step-600: #999999;
--ion-color-step-650: #a6a6a6;
--ion-color-step-700: #b3b3b3;
--ion-color-step-750: #bfbfbf;
--ion-color-step-800: #cccccc;
--ion-color-step-850: #d9d9d9;
--ion-color-step-900: #e6e6e6;
--ion-color-step-950: #f2f2f2;
--ion-item-background: #000000;
--ion-card-background: #1c1c1d;
}
body.dark.ios ion-modal {
--ion-background-color: var(--ion-color-step-100);
--ion-toolbar-background: var(--ion-color-step-150);
--ion-toolbar-border-color: var(--ion-color-step-250);
}
body.dark.md {
--ion-background-color: #121212;
--ion-background-color-rgb: 18, 18, 18;
--ion-text-color: #ffffff;
--ion-text-color-rgb: 255, 255, 255;
--ion-border-color: #222222;
--ion-color-step-50: #1e1e1e;
--ion-color-step-100: #2a2a2a;
--ion-color-step-150: #363636;
--ion-color-step-200: #414141;
--ion-color-step-250: #4d4d4d;
--ion-color-step-300: #595959;
--ion-color-step-350: #656565;
--ion-color-step-400: #717171;
--ion-color-step-450: #7d7d7d;
--ion-color-step-500: #898989;
--ion-color-step-550: #949494;
--ion-color-step-600: #a0a0a0;
--ion-color-step-650: #acacac;
--ion-color-step-700: #b8b8b8;
--ion-color-step-750: #c4c4c4;
--ion-color-step-800: #d0d0d0;
--ion-color-step-850: #dbdbdb;
--ion-color-step-900: #e7e7e7;
--ion-color-step-950: #f3f3f3;
--ion-item-background: #1e1e1e;
--ion-toolbar-background: #1f1f1f;
--ion-tab-bar-background: #1f1f1f;
--ion-card-background: #1e1e1e;
}

BIN
app/static/logo.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 KiB

View file

@ -1,12 +1,6 @@
{ {
"extends": "./.svelte-kit/tsconfig.json", "extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": { "compilerOptions": {
"typeRoots": [
"./node_modules/ionic-svelte"
],
"types": [
"ionic-svelte"
],
"allowJs": true, "allowJs": true,
"checkJs": true, "checkJs": true,
"esModuleInterop": true, "esModuleInterop": true,

View file

@ -0,0 +1,24 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("i42xt9r6ykqdq3j")
collection.listRule = ""
collection.viewRule = ""
collection.createRule = ""
collection.updateRule = ""
collection.deleteRule = ""
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("i42xt9r6ykqdq3j")
collection.listRule = null
collection.viewRule = null
collection.createRule = null
collection.updateRule = null
collection.deleteRule = null
return dao.saveCollection(collection)
})

View file

@ -0,0 +1,24 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("pf13z4hs1iubw41")
collection.listRule = ""
collection.viewRule = ""
collection.createRule = ""
collection.updateRule = ""
collection.deleteRule = ""
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("pf13z4hs1iubw41")
collection.listRule = null
collection.viewRule = null
collection.createRule = null
collection.updateRule = null
collection.deleteRule = null
return dao.saveCollection(collection)
})

View file

@ -0,0 +1,24 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("jebj4y3fqo5gtte")
collection.listRule = ""
collection.viewRule = ""
collection.createRule = ""
collection.updateRule = ""
collection.deleteRule = ""
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("jebj4y3fqo5gtte")
collection.listRule = null
collection.viewRule = null
collection.createRule = null
collection.updateRule = null
collection.deleteRule = null
return dao.saveCollection(collection)
})

View file

@ -0,0 +1,57 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((db) => {
const collection = new Collection({
"id": "jqwhs45vpzq2ig5",
"created": "2023-12-09 10:48:31.784Z",
"updated": "2023-12-09 10:48:31.784Z",
"name": "icons",
"type": "base",
"system": false,
"schema": [
{
"system": false,
"id": "ne3yufop",
"name": "name",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "dulswobh",
"name": "image",
"type": "file",
"required": false,
"presentable": false,
"unique": false,
"options": {
"maxSelect": 1,
"maxSize": 5242880,
"mimeTypes": [],
"thumbs": [],
"protected": false
}
}
],
"indexes": [],
"listRule": null,
"viewRule": null,
"createRule": null,
"updateRule": null,
"deleteRule": null,
"options": {}
});
return Dao(db).saveCollection(collection);
}, (db) => {
const dao = new Dao(db);
const collection = dao.findCollectionByNameOrId("jqwhs45vpzq2ig5");
return dao.deleteCollection(collection);
})

View file

@ -0,0 +1,33 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("pf13z4hs1iubw41")
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "zvbjddln",
"name": "icon",
"type": "relation",
"required": false,
"presentable": false,
"unique": false,
"options": {
"collectionId": "jqwhs45vpzq2ig5",
"cascadeDelete": false,
"minSelect": null,
"maxSelect": 1,
"displayFields": null
}
}))
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("pf13z4hs1iubw41")
// remove
collection.schema.removeField("zvbjddln")
return dao.saveCollection(collection)
})

View file

@ -0,0 +1,16 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("jqwhs45vpzq2ig5")
collection.viewRule = ""
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("jqwhs45vpzq2ig5")
collection.viewRule = null
return dao.saveCollection(collection)
})