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';
const appId = 'app.ionic.io';
const appName = 'app';
const appId = 'dev.korz.musclecat';
const appName = 'Musclecat';
const server = process.argv.includes('-hmr') ? {
'url': 'http://192.168.178.25:5173', // always have http:// in url
'cleartext': true

100
app/package-lock.json generated
View file

@ -9,9 +9,6 @@
"version": "0.0.1",
"dependencies": {
"@capacitor/core": "^5.5.1",
"@ionic/core": "^7.2.1",
"ionic-svelte": "^0.5.82",
"ionicons": "^7.2.1",
"pocketbase": "^0.19.0"
},
"devDependencies": {
@ -561,36 +558,6 @@
"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": {
"version": "2.1.6",
"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==",
"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": {
"version": "2.0.3",
"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_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": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
@ -3841,11 +3767,6 @@
"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": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
@ -4100,27 +4021,6 @@
"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": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz",

View file

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

View file

@ -4,6 +4,10 @@
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<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%
</head>
<body data-sveltekit-preload-data="hover">

View file

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

View file

@ -1,48 +1,46 @@
<script lang="ts">
import { setupIonicBase } from 'ionic-svelte';
/* Call Ionic's setup routine */
setupIonicBase();
/* Import all components - or do partial loading - see below */
import 'ionic-svelte/components/all';
import { goto } from '$app/navigation';
import { pb } from '$lib/api';
import '../theme/global.css';
/* Theme variables */
import '../theme/variables.css';
/*
This part - import 'ionic-svelte/components/all'; - loads all components at once.
async function goToRandomTask() {
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!!)
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
*/
let listsLoading = pb.collection("lists").getList(1, 50);
</script>
<ion-app>
<slot />
</ion-app>
<div class="topbar">
<div class="app-name">
<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>
<ion-card-header>
<ion-card-subtitle>Great success!!</ion-card-subtitle>
<ion-card-title>Welcome to your app!</ion-card-title>
</ion-card-header>
<script lang="ts">
import type { PageData } from './$types';
<ion-card-content>
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>
export let data: PageData;
<ion-item>
<ion-label>Visit Ionic Showcase app with sourceviewer</ion-label>
<ion-button href="https://ionicsvelte.firebaseapp.com/" target="_new" fill="outline" slot="end"
>View</ion-button
>
</ion-item>
let { tasks } = data;
</script>
<ion-item>
<ion-label>Visit Ionic component docs</ion-label>
<ion-button
href="https://ionicframework.com/docs/components"
target="_new"
fill="outline"
slot="end">View</ion-button
>
</ion-item>
<ion-item>
<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>
<h2>Haushalt</h2>
<ul class="task-list">
{#each tasks.items as task}
<li>
{task.name}
<span>{task.expand?.list.name}</span>
</li>
{/each}
</ul>
<button>Neue Aufgabe</button>

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">
import { pb } from "$lib/api";
import { pb } from '$lib/api';
let email = '';
let password = '';
const handleLogin = async (event: Event) => {
// Prevent the form from submitting normally
event.preventDefault();
email = email.trim();
password = password.trim();
email = email.trim();
password = password.trim();
// Replace with your own login logic
if (email && password) {
console.log('Login submitted:', email, password);
const resp = await pb.collection("users").authWithPassword(email, password);
console.log('Logged in:', resp);
const resp = await pb.collection('users').authWithPassword(email, password);
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>
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>Login</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<form on:submit={handleLogin}>
<ion-item>
<ion-label position="stacked">Email</ion-label>
<ion-input
type="email"
placeholder="Enter your email"
value={email}
on:ionInput={handleEmailInput}
required
>
</ion-input>
</ion-item>
<ion-item>
<ion-label position="stacked">Password</ion-label>
<ion-input
type="password"
placeholder="Enter your password"
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>
<form class="login-container" on:submit={handleLogin}>
<h1>Musceclat Login</h1>
<div class="form-group">
<label for="email">E-Mail:</label>
<input
type="email"
id="email"
name="email"
placeholder="Enter your email"
required
bind:value={email}
/>
</div>
<div class="form-group">
<label for="password">Password:</label>
<input
type="password"
id="password"
name="password"
placeholder="Enter your password"
required
bind:value={password}
/>
</div>
<button type="submit">Login</button>
</form>
<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>

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",
"compilerOptions": {
"typeRoots": [
"./node_modules/ionic-svelte"
],
"types": [
"ionic-svelte"
],
"allowJs": true,
"checkJs": 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)
})