Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified .github/main_page.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion .github/workflows/create-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,4 @@ jobs:
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
platforms: linux/amd64
platforms: linux/amd64,linux/arm64
2 changes: 1 addition & 1 deletion .github/workflows/push-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,4 @@ jobs:
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
platforms: linux/amd64
platforms: linux/amd64,linux/arm64
6 changes: 4 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
services:
app:
container_name: dashlit
image: dashlit:latest
container_name: dashlit-app
image: ghcr.io/codewec/dashlit:latest
restart: unless-stopped
environment:
ORIGIN: '${ORIGIN:-http://localhost:3000}' # please provide URL if different
ports:
- '3000:3000'
volumes:
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"dependencies": {
"@thisux/sveltednd": "^0.0.20",
"dotenv": "^16.6.0",
"jose": "^6.0.11"
"jose": "^6.0.11",
"svelte-5-french-toast": "^2.0.4"
}
}
22 changes: 22 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions src/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,11 @@
display: none;
}
}

.toaster {
.wrapper {
.base {
@apply bg-gray-100 dark:bg-slate-900 dark:text-gray-400;
}
}
}
5 changes: 4 additions & 1 deletion src/app.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@
<meta name="viewport" content="width=device-width, initial-scale=1" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<body
data-sveltekit-preload-data="hover"
class="bg-gray-50 text-gray-900 dark:bg-gray-950 dark:text-gray-200"
>
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>
2 changes: 0 additions & 2 deletions src/hooks.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ export const handle: Handle = async ({ event, resolve }) => {

const token = event.cookies.get(cookie_token_key);
if (!token) {
console.log('!!!!! not has token', token);
return resolve(event);
}

Expand All @@ -24,7 +23,6 @@ export const handle: Handle = async ({ event, resolve }) => {
event.locals.userAuthenticated = true;
})
.catch(() => {
console.log('!!!!! cant check jwt', token);
event.cookies.delete(cookie_token_key, { path: '/' });
});

Expand Down
4 changes: 2 additions & 2 deletions src/lib/components/actionButtons.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
onclick={() => {
handleClick(ActionType.EDIT);
}}
class="h-4 w-4 cursor-pointer p-3!"><EditOutline class="h-4 w-4" /></Button
class="h-4 w-4 cursor-pointer p-3!"><EditOutline class="h-4 w-4" color="gray" /></Button
>
<Button
pill={true}
Expand All @@ -41,6 +41,6 @@
onclick={() => {
handleClick(ActionType.DELETE);
}}
class="h-4 w-4 cursor-pointer p-3!"><CloseCircleOutline class="h-4 w-4" /></Button
class="h-4 w-4 cursor-pointer p-3!"><CloseCircleOutline class="h-4 w-4" color="gray" /></Button
>
</div>
52 changes: 22 additions & 30 deletions src/lib/components/dashboard.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import { flip } from 'svelte/animate';
import { fade } from 'svelte/transition';
import ActionButtons from './actionButtons.svelte';
import { getIds, hasField } from '$lib/helpers';
import { getIds, hasField, isUrlString } from '$lib/helpers';
import EmptyItem from './emptyItem.svelte';
import EmptyGroup from './emptyGroup.svelte';
import { newGroup, newItem } from '$lib/factory';
Expand Down Expand Up @@ -123,7 +123,7 @@
{#each groups as group (`g_${group.id}`)}
<div
class:edit-mode={editMode}
class="rounded-md bg-gray-50 p-4 shadow-sm ring-1 ring-gray-200"
class="rounded-md bg-gray-50 p-4 shadow-sm ring-1 ring-gray-200 dark:bg-slate-900 dark:ring-slate-800"
use:draggable={{
container: group.id,
dragData: group,
Expand All @@ -144,7 +144,7 @@
>
<div class="mb-4 flex items-center justify-between">
<div class="inline-flex gap-2">
<h2 class="font-semibold text-gray-900 capitalize">
<h2 class="font-semibold text-gray-900 capitalize dark:text-gray-200">
{group.title}
</h2>
{#if group.description}
Expand Down Expand Up @@ -200,11 +200,19 @@
<div class="flex items-center gap-2">
{#if item.icon}
<div class="h-14 w-14">
<Icon color="gray" icon={item.icon} height={56} />
{#if isUrlString(item.icon)}
<img
src={item.icon}
alt={item.title}
class="h-full w-full rounded-full object-cover"
/>
{:else}
<Icon color="gray" icon={item.icon} height={56} />
{/if}
</div>
{/if}
<div>
<h3 class="font-medium text-gray-900">
<h3 class="font-medium text-gray-900 dark:text-gray-100">
{item.title}
</h3>
<p class="text-sm text-gray-500">
Expand All @@ -227,6 +235,10 @@
{/each}
{#if editMode}
<EmptyItem
id={`${group.id}-0`}
handleHover={(id) => {
hoveredId = id;
}}
handleClick={() => handleClickItemAction(ActionType.CREATE, group.id, newItem())}
/>
{/if}
Expand All @@ -242,42 +254,22 @@
}

:global(.drag-over) {
@apply !bg-blue-50 !ring-2 !ring-blue-400;
@apply !bg-blue-50 !ring-2 !ring-blue-400 dark:!bg-slate-800 dark:ring-blue-600;
}

.item {
@apply relative rounded-lg bg-white p-3 shadow-sm ring-1 ring-gray-200 transition-all duration-200;
@apply relative rounded-lg bg-white p-3 shadow-sm ring-1 ring-gray-200 transition-all duration-200 dark:bg-black dark:ring-gray-800;
}

.item:not(.edit-mode) {
@apply cursor-pointer hover:shadow-md hover:ring-2 hover:ring-blue-300;
@apply cursor-pointer hover:shadow-md hover:ring-2 hover:ring-blue-300 dark:hover:ring-blue-900;
}

.edit-mode {
animation: tilt-shaking 0.3s infinite;
@apply cursor-move hover:shadow-md hover:ring-2 hover:ring-blue-200;
@apply cursor-move hover:shadow-md hover:ring-2 hover:ring-blue-200 dark:hover:ring-blue-900;

.item {
animation: tilt-shaking 0.2s infinite;
@apply cursor-move hover:shadow-md hover:ring-2 hover:ring-blue-200;
}
}

@keyframes tilt-shaking {
0% {
transform: rotate(0deg);
}
25% {
transform: rotate(0.3deg);
}
50% {
transform: rotate(0eg);
}
75% {
transform: rotate(-0.3deg);
}
100% {
transform: rotate(0deg);
@apply cursor-move hover:shadow-md hover:ring-2 hover:ring-blue-200 dark:hover:ring-blue-900;
}
}
</style>
16 changes: 15 additions & 1 deletion src/lib/components/emptyItem.svelte
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
<script lang="ts">
import Button from 'flowbite-svelte/Button.svelte';
const { handleClick }: { handleClick: () => void } = $props();
const {
id,
handleHover,
handleClick
}: {
id: string;
handleHover: (id: string | undefined) => void;
handleClick: () => void;
} = $props();
</script>

<Button
onmouseenter={() => {
handleHover(id);
}}
onmouseleave={() => {
handleHover(undefined);
}}
onclick={handleClick}
color="light"
class="cursor-pointer rounded-lg border border-dashed border-gray-300 bg-transparent p-3 text-center text-sm text-gray-500 ring-0 focus-within:ring-0 hover:border-gray-500"
Expand Down
34 changes: 20 additions & 14 deletions src/lib/components/header.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script lang="ts">
import { ButtonGroup, GradientButton, Button } from 'flowbite-svelte';
import { ButtonGroup, GradientButton, Button, DarkMode } from 'flowbite-svelte';

let {
editMode,
Expand All @@ -11,17 +11,23 @@
</script>

<div class="mb-8 flex justify-between gap-2">
<h1 class="text-2xl font-bold text-gray-900">DashLit</h1>
<ButtonGroup size="sm" class="*:ring-0!">
{#if editMode}
<GradientButton color="purpleToBlue" class="hover:text-white" onclick={() => handleSave()}
>Save</GradientButton
>
{:else}
<Button onclick={() => handleEdit()}>Edit</Button>
{/if}
{#if canLogout}
<Button href="/logout">Logout</Button>
{/if}
</ButtonGroup>
<h1 class="text-2xl font-bold">DashLit</h1>

<div class="inline-flex items-center">
<DarkMode size="sm" class="cursor-pointer p-2" />
<ButtonGroup size="sm" class="*:ring-0!">
{#if editMode}
<GradientButton
color="purpleToBlue"
class="cursor-pointer hover:text-white"
onclick={() => handleSave()}>Save</GradientButton
>
{:else}
<Button class="cursor-pointer" onclick={() => handleEdit()}>Edit</Button>
{/if}
{#if canLogout}
<Button data-sveltekit-preload-data="off" href="/logout">Logout</Button>
{/if}
</ButtonGroup>
</div>
</div>
4 changes: 2 additions & 2 deletions src/lib/components/modalFormItem.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@
</Label>
<Label class="space-y-2">
<span>Icon</span>
<Input bind:value={form.icon} type="text" name="icon" placeholder="Icon name" />
<Input bind:value={form.icon} type="text" name="icon" placeholder="URL or Icon name" />
<Helper class="text-sm">
Icon name from <a
URL or Icon name from <a
target="_blank"
href="https://icon-sets.iconify.design/"
class="text-primary-600 dark:text-primary-500 font-medium hover:underline">iconify</a
Expand Down
8 changes: 8 additions & 0 deletions src/lib/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,11 @@ export const generateRandomString = (length: number) => {
.toString(36)
.substring(2, 2 + length);
};

export const isUrlString = (str: string): boolean => {
if (!str) {
return false;
}
const urlRegex = /^(ftp|http|https):\/\/[^ "]+$/;
return urlRegex.test(str);
};
21 changes: 9 additions & 12 deletions src/routes/(auth)/login/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,27 +1,30 @@
<script lang="ts">
import { enhance } from '$app/forms';
import toast from 'svelte-5-french-toast';
import { page_title } from '$lib';
import { Card, Button, Label, Input, Helper } from 'flowbite-svelte';

let errorMessage: string | undefined = $state(undefined);
import { Card, Button, Label, Input, Helper, DarkMode } from 'flowbite-svelte';
</script>

<svelte:head>
<title>{page_title}</title>
</svelte:head>

<div
class="flex h-screen items-center justify-center bg-gradient-to-r from-indigo-200 via-purple-200 to-pink-200 p-4"
class="flex h-screen items-center justify-center bg-gradient-to-r from-blue-50 via-indigo-50 to-sky-50 p-4 dark:from-slate-950 dark:via-gray-950 dark:to-zinc-950"
>
<DarkMode class="absolute top-4 right-4 cursor-pointer" />
<Card class="p-4 sm:p-6 md:p-8">
<form
method="POST"
use:enhance={() => {
errorMessage = undefined;
const toastId = toast.loading('Checking...');
return async ({ result, update }) => {
await update();
if (result.type === 'failure') {
errorMessage = (result.data?.error as string) ?? 'An error occurred';
const message = (result.data?.error as string) ?? 'An error occurred';
toast.error(message, { id: toastId });
} else {
toast.success('Welcome back!', { icon: '👋', id: toastId });
}
};
}}
Expand All @@ -31,12 +34,6 @@
<Label class="space-y-2">
<span>Your password</span>
<Input type="password" name="password" placeholder="•••••" required />
{#if errorMessage}
<Helper class="mt-2" color="red">
<span class="font-medium">Error: </span>
{errorMessage}
</Helper>
{/if}
</Label>
<Button type="submit" class="w-full">Login</Button>
</form>
Expand Down
Loading