Compare commits

..

No commits in common. "main" and "main" have entirely different histories.
main ... main

35 changed files with 3702 additions and 6761 deletions

View file

@ -2,13 +2,13 @@
"name": "swabsite",
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
"features": {
"ghcr.io/devcontainers/features/node": {}
"ghcr.io/devcontainers-community/features/deno": {}
},
"containerUser": "vscode",
"containerEnv": {
"ASTRO_TELEMETRY_DISABLED": "true"
},
"postStartCommand": "npm install",
"postStartCommand": "deno task install",
"runArgs": ["--userns=keep-id", "--security-opt=label=disable"],
"customizations": {
"vscode": {

View file

@ -1,34 +0,0 @@
name: deploy
on:
push:
branches:
- main
workflow_dispatch:
env:
repo: website
git: git.swablab.de
url: git.swablab.de/${{ github.repository }}
token: ${{ secrets.SWABLAB_ORGA_PACKAGE_TOKEN }}
ASTRO_TELEMETRY_DISABLED: true
jobs:
deploy:
name: deploy
runs-on: docker
container:
image: quay.io/buildah/stable:latest
env:
STORAGE_DRIVER: vfs
steps:
- name: download
run: curl https://${{ env.url }}/archive/main.tar.gz | tar -xz
- name: build
run: buildah build -f ${{ env.repo }}/Containerfile -t ${{ env.url }}:latest ${{ env.repo }}
- name: login
run: buildah login -u token -p ${{ env.token }} ${{ env.git }}
- name: push
run: buildah push ${{ env.git }}/${{ github.repository }}:latest

View file

@ -0,0 +1,53 @@
name: Build Astro
on: [push, workflow_dispatch]
env:
ASTRO_TELEMETRY_DISABLED: true
jobs:
check:
name: 🧪 Astro check
runs-on: docker
container:
image: node:lts
steps:
- uses: https://github.com/actions/checkout@v4
- uses: https://github.com/denoland/setup-deno@v2
with:
deno-version: v2.x
- run: deno task install
- run: deno task check
deploy:
name: 🚢 Deploy
runs-on: docker
container:
image: node:lts
if: github.ref == 'refs/heads/main'
needs: [check]
steps:
- uses: https://github.com/actions/checkout@v4
- uses: https://github.com/denoland/setup-deno@v2
with:
deno-version: v2.x
- run: deno task install
- run: deno task build
- name: build containerfile
id: build-image
uses: https://github.com/redhat-actions/buildah-build@v2
with:
image: website
tags: latest
containerfiles: |
./Containerfile
- name: Push to registry
uses: https://github.com/redhat-actions/push-to-registry@v2
with:
image: ${{ steps.build-image.outputs.image }}
tags: ${{ steps.build-image.outputs.tags }}
username: token
password: ${{ secrets.GITHUB_TOKEN }}
registry: ${{ github.server_url }}/${{ github.repository }}

View file

@ -1,5 +1,6 @@
{
"recommendations": [
"astro-build.astro-vscode"
"astro-build.astro-vscode",
"denoland.vscode-deno"
]
}
}

24
.vscode/tasks.json vendored
View file

@ -2,43 +2,35 @@
"version": "2.0.0",
"tasks": [
{
"label": "install",
"command": "npm install",
"type": "shell",
"problemMatcher": []
},
{
"label": "update",
"command": "npm update -S",
"label": "install/update",
"command": "deno task install",
"type": "shell",
"problemMatcher": []
},
{
"label": "dev",
"command": "npm run dev",
"command": "deno task dev",
"type": "shell",
"problemMatcher": []
},
{
"label": "check",
"command": "npm run check",
"command": "deno task check",
"type": "shell",
"problemMatcher": []
},
{
"label": "preview",
"command": "npm run preview",
"command": "deno task preview",
"type": "shell",
"problemMatcher": [],
"dependsOn": [
"build"
]
"dependsOn": ["build"]
},
{
"label": "build",
"command": "npm run build",
"command": "deno task build",
"type": "shell",
"problemMatcher": []
}
]
}
}

View file

@ -1,13 +1,6 @@
FROM docker.io/library/node AS build
WORKDIR /app
COPY . .
ENV ASTRO_TELEMETRY_DISABLED=true
RUN npm install
RUN npm run build
FROM ghcr.io/swablab/documents:latest AS documents
FROM git.swablab.de/swablab/documents AS documents
FROM docker.io/nginxinc/nginx-unprivileged
FROM docker.io/nginxinc/nginx-unprivileged:latest
COPY ./nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=build /app/dist /usr/share/nginx/html
COPY ./dist /usr/share/nginx/html
COPY --from=documents / /usr/share/nginx/html/docs

16
README.md Normal file
View file

@ -0,0 +1,16 @@
# swablab.de
[![Build Astro CI](https://github.com/swablab/website/actions/workflows/main.yml/badge.svg?branch=main)](https://github.com/swablab/website/actions/workflows/main.yml)
These are the source files for the page published at https://swablab.de.
Powered by [Astro](https://astro.build)
# Installation
We recommend [deno](https://deno.com/) as the package manager. To install all
required packages, execute `deno task install`.
Afterwards, you can develop with `deno task dev` and build with
`deno task build`. It is recommended to use VSCode as your editor, as all tasks
can be executed easily within VSCode.

View file

@ -1,5 +1,5 @@
import sitemap from "@astrojs/sitemap"
import tailwindcss from "@tailwindcss/vite"
import tailwind from "@astrojs/tailwind"
import { defineConfig, passthroughImageService } from "astro/config"
export default defineConfig({
@ -8,14 +8,9 @@ export default defineConfig({
server: {
host: true,
},
integrations: [sitemap()],
integrations: [tailwind(), sitemap()],
image: {
domains: ["directus.swablab.de", "files.mastodon.social"],
service: passthroughImageService(),
},
vite: {
plugins: [
tailwindcss(),
],
},
})

8
deno.json Normal file
View file

@ -0,0 +1,8 @@
{
"fmt": {
"options": {
"semiColons": false,
"indentWidth": 2
}
}
}

3023
deno.lock generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -17,11 +17,8 @@ server {
internal;
}
location /todo {
return 302 https://directus.swablab.de/admin/content/tasks_general;
}
location /discord {
return 302 https://discord.gg/A4grfc5Vzm;
return 301 https://discord.gg/sZbmJdGkMQ;
}
location / {

6536
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -2,6 +2,7 @@
"name": "@swablab/website",
"private": true,
"scripts": {
"install": "deno install -q",
"check": "astro check",
"dev": "astro dev",
"build": "astro build",
@ -9,19 +10,22 @@
},
"dependencies": {
"@astrojs/check": "^0.9.4",
"@astrojs/sitemap": "^3.3.0",
"@fontsource-variable/ubuntu-sans": "^5.2.6",
"@iconify-json/ph": "^1.2.2",
"@iconify/tailwind4": "^1.0.6",
"@tailwindcss/typography": "^0.5.16",
"@tailwindcss/vite": "^4.0.15",
"astro": "^5.5.4",
"daisyui": "^5.0.9",
"tailwindcss": "^4.0.14"
"@astrojs/sitemap": "^3.2.1",
"@astrojs/tailwind": "^5.1.3",
"@fontsource-variable/ubuntu-sans": "^5.1.0",
"@iconify-json/ph": "^1.2.1",
"@iconify/tailwind": "^1.1.3",
"@tailwindcss/typography": "^0.5.15",
"@types/alpinejs": "^3.13.11",
"alpinejs": "^3.14.6",
"astro": "^5.0.3",
"daisyui": "^4.12.14",
"tailwindcss": "^3.4.16",
"typescript": "^5.7.2"
},
"prettier": {
"tabWidth": 2,
"semi": false,
"endOfLine": "lf"
}
}
}

5
public/test/me Normal file
View file

@ -0,0 +1,5 @@
{
"data": {
"id": "user1"
}
}

7
public/test/presence Normal file
View file

@ -0,0 +1,7 @@
{
"data": {
"id": 1,
"last": "2024-05-08T18:00:00",
"next": "2024-05-08T18:00:00"
}
}

25
public/test/task Normal file
View file

@ -0,0 +1,25 @@
{
"data": {
"id": "task",
"status": "in_progress",
"title": "title 1",
"date_due": "2024-01-01T12:00:00",
"labels": [
"label1"
],
"description": "description 1",
"priority": "high",
"responsibles": [
{
"directus_users_id": {
"id": "user1"
}
},
{
"directus_users_id": {
"id": "user2"
}
}
]
}
}

37
public/test/tasks_general Normal file
View file

@ -0,0 +1,37 @@
{
"data": [
{
"id": "task",
"status": "in_progress",
"title": "title1",
"date_due": "2024-01-01T12:00:00",
"labels": [
"label1"
],
"description": "description1",
"priority": "high",
"responsibles": [
{
"directus_users_id": {
"id": "user1"
}
},
{
"directus_users_id": {
"id": "user2"
}
}
]
},
{
"id": "task",
"status": "in_progress",
"title": "title2",
"date_due": null,
"labels": [],
"description": "description2",
"priority": "medium",
"responsibles": []
}
]
}

12
public/test/users Normal file
View file

@ -0,0 +1,12 @@
{
"data": [
{
"id": "user1",
"first_name": "first_name1"
},
{
"id": "user2",
"first_name": "first_name2"
}
]
}

View file

@ -34,7 +34,7 @@ const { name, image, link, small } = Astro.props
<span class="card-title">{name}</span>
)
}
<p class="text-lg">
<p>
<slot />
</p>
</div>

View file

@ -71,7 +71,7 @@ function replaceEmojis(content: string) {
</figure>
)}
<div class="card-body text-left text-lg">
<div class="card-body text-left">
<h2 class="card-title justify-between">
<a href={post.url}>@swablab</a>
<time class="text-xs opacity-50">

View file

@ -5,7 +5,6 @@ import DirectusImg from "./DirectusImg.astro"
type Member = {
firstname: string
image?: string
chairman?: boolean
}
const members = await directus<Member[]>("items/members?sort=firstname")
---
@ -25,59 +24,27 @@ const members = await directus<Member[]>("items/members?sort=firstname")
</div>
</div>
<div class="text-2xl md:text-4xl pt-8">Vorstand</div>
<div
class="flex flex-row flex-wrap gap-6 justify-evenly sm:justify-center text-base"
class="flex flex-row flex-wrap gap-6 justify-evenly sm:justify-center text-base pt-8"
>
{
members
.filter((m) => m.chairman)
.map((member) => (
<div>
<DirectusImg
src={
member.image != null
? member.image
: "a8f48962-9f0e-40e6-abd2-e932aa9dea2e"
}
widths={[200]}
format="webp"
alt={"Profilbild von " + member.firstname}
class="rounded-full w-[100px] h-[100px]"
/>
members.map((member) => (
<div>
<DirectusImg
src={
member.image != null
? member.image
: "a8f48962-9f0e-40e6-abd2-e932aa9dea2e"
}
widths={[200]}
format="webp"
alt={"Profilbild von " + member.firstname}
class="rounded-full w-[100px] h-[100px]"
/>
<p>{member.firstname}</p>
</div>
))
}
</div>
<div class="text-2xl md:text-4xl pt-8">Mitglieder</div>
<div
class="flex flex-row flex-wrap gap-6 justify-evenly sm:justify-center text-base"
>
{
members
.filter((m) => !m.chairman)
.map((member) => (
<div>
<DirectusImg
src={
member.image != null
? member.image
: "a8f48962-9f0e-40e6-abd2-e932aa9dea2e"
}
widths={[200]}
format="webp"
alt={"Profilbild von " + member.firstname}
class="rounded-full w-[100px] h-[100px]"
/>
<p>{member.firstname}</p>
</div>
))
<p>{member.firstname}</p>
</div>
))
}
<div>

View file

@ -7,7 +7,7 @@ const { title, jumpId }: Props = Astro.props
---
<div class="text-center p-8 space-y-8 even:bg-base-200">
<a class="invisible relative -top-24" id={jumpId}></a>
<a hidden class="block invisible relative -top-20" id={jumpId}></a>
<div class="text-3xl md:text-5xl">
<a href={"#" + jumpId}>{title}</a>
</div>

View file

@ -1,7 +1,33 @@
export async function directus<T>(path: string): Promise<T> {
return await fetch(`https://directus.swablab.de/${path}`)
const env = (import.meta as unknown as {
env: {
DEV: boolean
SSR: boolean
}
}).env
export async function directus<T>(
path: string,
method: string = "GET",
body: string | null = null,
): Promise<T> {
return await fetch(
env.DEV && !env.SSR
? `/test/${path.split("/").at(-1)}`
: `https://directus.swablab.de/${path}`,
{
method,
credentials: "include",
headers: {
"Content-Type": "application/json",
},
body,
},
)
.then((res) => res.json())
.then((res) => res.data)
.then((res) => {
if (res.errors) throw res.errors[0].message
return res.data
})
}
export function formatDate(
@ -32,3 +58,18 @@ export const documents = {
"Werkstatt-AGB": "/docs/werkstatt-agb.pdf",
"Werkstatt-Regeln": "/docs/werkstatt-regeln.pdf",
}
export type Task = {
id: string | undefined
title: string
status: string
date_due?: string
priority: string
description: string
responsibles: {
directus_users_id: {
id: string
first_name: string
}
}[]
}

View file

@ -1,16 +1,16 @@
---
import "../style.css"
import "../style.css";
export interface Props {
title?: string
description?: string
title?: string;
description?: string;
}
const { title } = Astro.props
const { title } = Astro.props;
const titleFull = title == null ? "swablab e.V." : title + " | swablab e.V."
const titleFull = title == null ? "swablab e.V." : title + " | swablab e.V.";
const description =
"swablab e.V. in Freudenstadt - die offene Werkstatt für alle ambitionierten Hobby-Schreiner, Bastler, Tüftler, Elektroniker und vieles mehr."
"swablab e.V. in Freudenstadt - die offene Werkstatt für alle ambitionierten Hobby-Schreiner, Bastler, Tüftler, Elektroniker und vieles mehr.";
---
<html lang="de-DE" class="scroll-smooth">
@ -29,6 +29,13 @@ const description =
<title>{titleFull}</title>
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<script
is:inline
defer
data-domain="swablab.de"
src="https://analytics.swablab.de/js/plausible.outbound-links.js"
></script>
</head>
<body class="min-h-screen flex flex-col">
<slot />

View file

@ -28,9 +28,9 @@ const social = [
"https://www.printables.com/social/103546-swablab-ev/about",
),
entry(
"Forgejo",
"icon-[ph--git-branch]",
"https://git.swablab.de/explore/repos",
"GitHub",
"icon-[ph--github-logo-duotone]",
"https://github.com/swablab",
),
]

View file

@ -14,14 +14,10 @@ const links = [
<nav class="fixed top-0 navbar z-30 glass bg-base-100/30">
<div class="navbar-start">
<ul class="menu menu-horizontal p-0">
<li>
<a class="text-xl" href="/">
<img class="w-8 h-8" src="/logo.svg" alt="" />
<span>swablab</span>
</a>
</li>
</ul>
<a class="btn btn-ghost text-xl font-normal" href="/">
<img class="w-8 h-8" src="/logo.svg" alt="swablab logo" />
<span>swablab</span></a
>
</div>
<div class="navbar-center hidden lg:flex">
<ul class="menu menu-horizontal p-0">
@ -38,12 +34,6 @@ const links = [
</li>
))
}
<li>
<a class="text-black bg-primary" href="https://wiki.swablab.de">
<span class="w-5 h-5 icon-[ph--notebook-duotone]"></span>
wiki
</a>
</li>
</ul>
</div>
<div class="navbar-end">
@ -60,12 +50,6 @@ const links = [
</li>
))
}
<li>
<a class="text-primary" href="https://wiki.swablab.de">
<span class="w-5 h-5 icon-[ph--notebook-duotone]"></span>
wiki
</a>
</li>
</ul>
</details>
</div>

View file

@ -1,13 +1,13 @@
<div id="presence" class="hidden">
<div class="xl:hidden">
<div class="md:hidden">
<div class="tooltip tooltip-left">
<button class="btn btn-square">
<button class="btn btn-square font-normal">
<span class="icon-[ph--door-open-duotone] w-6 h-6"></span>
</button>
</div>
</div>
<div class="hidden xl:flex">
<button class="btn">
<div class="hidden md:flex">
<button class="btn font-normal">
<span id="presence-text"></span>
<span class="icon-[ph--door-open-duotone] w-6 h-6"></span>
</button>

View file

@ -82,7 +82,7 @@ import Page from "../layouts/Page.astro"
],
[
"Sommer 2024",
`Neben unserem Instagram-Account werden jetzt Neuigkeiten und Projekte auch auf Mastodon (@swablab@mastodon.social) geposted.`,
`Neben unserem Instagram-Account werden jetzt Neuigkeiten und Projekte auch auf Mastodon (@swablab) geposted.`,
],
[
"Herbst 2024",

View file

@ -14,11 +14,11 @@ import Page from "../layouts/Page.astro"
kannst du an folgendes Konto deinen Spendenbeitrag überweisen:
</Text>
<div class="stats shadow bg-base-300">
<div class="stats shadow">
<div class="stat">
<div class="stat-title text-xl">IBAN: DE18 6039 1310 0125 6340 05</div>
<div class="stat-title text-xl">BIC: GENODES1VBH</div>
<div class="stat-title text-xl">Bank: Volksbank in der Region</div>
<div class="stat-title">IBAN: DE18 6039 1310 0125 6340 05</div>
<div class="stat-title">BIC: GENODES1VBH</div>
<div class="stat-title">Bank: Volksbank in der Region</div>
</div>
</div>

View file

@ -15,8 +15,7 @@ Telefon: [+49 15679 232971](tel:+4915679232971)
**Vertreten durch**\
Fabian Haas\
Manuel Knodel\
Bastian Wittke
Manuel Knodel
**Registereintrag**\
Amtsgericht Stuttgart\

View file

@ -24,11 +24,11 @@ import Page from "../layouts/Page.astro"
oder ihn an folgende Adresse schicken:
</Text>
<div class="stats shadow bg-base-300">
<div class="stats shadow">
<div class="stat">
<div class="stat-value">swablab e.V.</div>
<div class="stat-title text-xl">Katharinenstr. 1</div>
<div class="stat-title text-xl">72250 Freudenstadt</div>
<div class="stat-title">Katharinenstr. 1</div>
<div class="stat-title">72250 Freudenstadt</div>
</div>
</div>
</Section>

207
src/pages/todo-edit.astro Normal file
View file

@ -0,0 +1,207 @@
---
import Base from "../layouts/Base.astro"
const statuses = [
{ id: "not_started", title: "Nicht gestartet" },
{ id: "in_progress", title: "In Arbeit" },
{ id: "hold", title: "Wartend" },
{ id: "done", title: "Abgeschlossen" },
]
const priorities = [
{ id: "low", title: "Niedrig" },
{ id: "normal", title: "Normal" },
{ id: "high", title: "Hoch" },
]
---
<Base title="Todo">
<div x-data="task" x-cloak>
<div class="navbar glass">
<div class="navbar-start">
<a class="btn btn-ghost text-xl">todo</a>
</div>
<div class="navbar-end space-x-2">
<a class="btn btn-success btn-square" x-on:click="submit()">
<span class="icon-[ph--floppy-disk] w-6 h-6"></span>
</a>
<a class="btn btn-error btn-square" href="/todo">
<span class="icon-[ph--x] w-6 h-6"></span>
</a>
</div>
</div>
<div x-show="error" class="p-4 flex flex-col space-y-4">
<div
x-show="!error.includes('permission to access collection')"
class="alert alert-error"
>
<span x-text="error"></span>
</div>
<a
x-show="error.includes('permission to access collection')"
class="btn btn-primary"
href="https://directus.swablab.de/auth/login/zitadel?redirect=https://swablab.de/todo"
>
Klicke hier, um dich anzumelden
</a>
</div>
<div class="drawer">
<input id="drawer" type="checkbox" class="drawer-toggle" />
<div class="drawer-content">
<div class="p-4 grid md:grid-cols-3 gap-4">
<input x-model="title" class="input input-bordered md:col-span-3" />
<label class="form-control">
<div class="label">Status</div>
<select x-model="status" class="select select-bordered">
{
statuses.map((status) => (
<option value={status.id}>{status.title}</option>
))
}
</select>
</label>
<label class="form-control">
<div class="label">Priorität</div>
<select x-model="priority" class="select select-bordered">
{
priorities.map((prio) => (
<option value={prio.id}>{prio.title}</option>
))
}
</select>
</label>
<label class="form-control">
<div class="label">Fälligkeitdatum</div>
<input
x-model="date_due"
class="input input-bordered"
type="date"
/>
</label>
<div class="form-control md:col-span-3">
<div class="label space-x-2 justify-start">
<span>Verantwortliche</span>
<label for="drawer" class="btn btn-sm btn-primary btn-square">
+
</label>
</div>
<div class="flex space-x-2">
<template x-for="res in responsibles">
<button
class="btn"
x-on:click="responsibles = responsibles.filter(x => x != res)"
x-text="users.find(u => u.id == res)?.first_name ?? res"
>
</button>
</template>
</div>
</div>
<label class="form-control md:col-span-3 h-64">
<div class="label">Beschreibung</div>
<textarea
x-model="description"
class="textarea textarea-bordered md:col-span-3 h-64"
>
</textarea>
</label>
</div>
</div>
<div class="drawer-side">
<label for="drawer" class="drawer-overlay"></label>
<ul class="menu p-4 w-80 min-h-full bg-base-200 text-base-content">
<template x-for="user in users">
<li>
<a
x-on:click="responsibles.indexOf(user.id) == -1 && responsibles.push(user.id)"
x-text="user.first_name"
>
</a>
</li>
</template>
</ul>
</div>
</div>
</div>
</Base>
<script>
import Alpine from "alpinejs"
import { directus, Task } from "../helper"
Alpine.data("task", () => ({
id: globalThis.location.search.replace("?", ""),
title: "",
status: "not_started",
date_due: "",
priority: "normal",
description: "",
responsibles: [],
users: [],
error: "",
init() {
directus<{ id: string; first_name: string }[]>(
`users?fields=id,first_name&filter[provider][_neq]=default`,
).then(
(res) =>
(this.users = res.sort((a, b) =>
a.first_name.localeCompare(b.first_name),
)),
)
if (this.id != "") {
directus<Task>(
`items/tasks_general/${this.id}?fields=*,responsibles.directus_users_id.id`,
)
.then((res) => {
this.title = res.title
this.status = res.status
this.date_due = res.date_due?.substring(0, 10) ?? ""
this.priority = res.priority
this.description = res.description
this.responsibles = res.responsibles.map(
(x) => x.directus_users_id.id,
)
})
.catch((err) => (this.error = err))
}
},
submit() {
const task = {
id: this.id,
title: this.title,
status: this.status,
date_due: this.date_due != "" ? this.date_due : null,
priority: this.priority,
description: this.description,
responsibles: this.responsibles.map(
(r: { directus_users_id: string }) => ({
directus_users_id: r,
}),
),
}
if (this.id != "") {
directus(
`items/tasks_general/${this.id}`,
"PATCH",
JSON.stringify(task),
).then((_) => (document.location.href = "/todo"))
} else {
directus(`items/tasks_general`, "POST", JSON.stringify(task)).then(
(_) => (document.location.href = "/todo"),
)
}
},
}))
Alpine.start()
</script>

133
src/pages/todo.astro Normal file
View file

@ -0,0 +1,133 @@
---
import Base from "../layouts/Base.astro"
const statuses = [
{ id: "not_started", title: "Nicht gestartet" },
{ id: "in_progress", title: "In Arbeit" },
{ id: "hold", title: "Wartend" },
]
---
<Base title="Todo">
<div x-data="kanban" x-cloak>
<div class="navbar glass">
<div class="navbar-start">
<a class="btn btn-ghost text-xl">todo</a>
</div>
<div class="navbar-end">
<a class="btn btn-primary btn-square" href="/todo-edit">
<span class="icon-[ph--plus] w-6 h-6"></span>
</a>
</div>
</div>
<div x-show="error" class="p-4 flex flex-col space-y-4">
<div
x-show="!error.includes('permission to access collection')"
class="alert alert-error"
>
<span x-text="error"></span>
</div>
<a
x-show="error.includes('permission to access collection')"
class="btn btn-primary"
href="https://directus.swablab.de/auth/login/zitadel?redirect=https://swablab.de/todo"
>
Klicke hier, um dich anzumelden
</a>
</div>
<div class="p-4 flex flex-col space-y-2" x-show="!error">
<div class="join">
<input
class="input input-primary w-full join-item"
placeholder="Suchen nach Titel oder Mitglied..."
x-model="search"
/>
<button
class="btn btn-primary join-item"
x-on:click="search = `id:${me}`"
>
Nur meine Aufgaben
</button>
</div>
<ul class="menu grid lg:grid-cols-3 w-full bg-base-200 rounded-box">
{
statuses.map((status) => (
<li x-data={"tasklist('" + status.id + "')"} class="flex-auto">
<a class="pointer-events-none">{status.title}</a>
<ul>
<template x-for="task in sortedTasks()">
<li>
<a x-bind:href="`/todo-edit?${task.id}`">
<span
x-show="task.responsibles.find(x => x.directus_users_id.id == me)"
class="badge badge-xs badge-info"
/>
<span
x-bind:class="{'text-secondary': task.priority == 'high','text-neutral-500': task.priority == 'low'}"
x-text="task.title"
/>
<span
class="badge badge-xs"
x-bind:class="new Date(task.date_due ?? 0) < new Date() ? `badge-error`: `badge-ghost`"
x-show="task.date_due != null"
x-text="formatDate(task.date_due, `short`)"
/>
</a>
</li>
</template>
</ul>
</li>
))
}
</ul>
</div>
</div>
</Base>
<script>
import Alpine from "alpinejs"
import { directus, formatDate, Task } from "../helper"
Alpine.data("kanban", () => ({
me: "",
search: "",
error: "",
init() {
directus<{ id: string }>("users/me?fields=id")
.then((res) => (this.me = res.id))
.catch((err) => (this.error = err))
},
}))
Alpine.data("tasklist", (status: string) => ({
tasks: [],
formatDate: formatDate,
init() {
directus<Task[]>(
`items/tasks_general?filter[status][_eq]=${status}&fields=*,responsibles.directus_users_id.id,responsibles.directus_users_id.first_name`,
)
.then((res) => (this.tasks = res))
.catch((err) => (this.error = err))
},
sortedTasks() {
return (this.tasks as Task[])
.filter(
(a) =>
a.title.toLowerCase().includes(this.search.toLowerCase()) ||
a.responsibles
.map(
(x) =>
`${x.directus_users_id.first_name}|id:${x.directus_users_id.id}`,
)
.join("|")
.toLowerCase()
.includes(this.search.toLowerCase()),
)
.sort((a, b) => a.title.localeCompare(b.title))
.sort((a, b) => (a.date_due ?? "z").localeCompare(b.date_due ?? "z"))
},
}))
Alpine.start()
</script>

View file

@ -1,50 +1,13 @@
@import "tailwindcss";
@plugin "@tailwindcss/typography";
@theme {
--font-sans: "Ubuntu", "sans-serif";
--font-serif: "Ubuntu", "sans-serif";
--font-mono: "Ubuntu Mono", "monospace";
}
@plugin "@iconify/tailwind4";
@plugin "daisyui" {}
@plugin "daisyui/theme" {
name: "swablab";
default: true;
prefersdark: true;
input {
color-scheme: dark;
--color-base-content: white;
--color-base-100: "#171717";
--color-base-200: "#262626";
--color-base-300: "#404040";
--color-primary: "#A3FFF1";
--color-primary-content: black;
--color-secondary: "#FF7F50";
--color-primary-content: black;
--color-neutral: "#404040";
--radius-selector: 1rem;
--radius-field: 0.25rem;
--radius-box: 0.5rem;
--size-selector: 0.25rem;
--size-field: 0.25rem;
--border: 1px;
--depth: 0;
--noise: 0;
--glass-reflect-opacity: 0.001;
}
@font-face {
font-family: "Ubuntu";
font-style: normal;
font-display: block;
font-weight: 100 800;
src: url(@fontsource-variable/ubuntu-sans/files/ubuntu-sans-latin-wght-normal.woff2) format("woff2-variations");
src: url(@fontsource-variable/ubuntu-sans/files/ubuntu-sans-latin-wght-normal.woff2)
format("woff2-variations");
unicode-range:
U+0000-00FF,
U+0131,
@ -66,4 +29,8 @@
U+2215,
U+FEFF,
U+FFFD;
}
}
[x-cloak] {
display: none !important;
}

38
tailwind.config.ts Normal file
View file

@ -0,0 +1,38 @@
module.exports = {
content: ["./src/**/*"],
theme: {
extend: {
colors: {
primary: "#A3FFF1",
secondary: "#FF7F50",
},
fontFamily: {
sans: ["Ubuntu", "sans-serif"],
serif: ["Ubuntu", "sans-serif"],
mono: ["Ubuntu Mono", "monospace"],
},
},
},
daisyui: {
themes: [
{
swablab: {
primary: "#A3FFF1",
secondary: "#FF7F50",
neutral: "#404040",
"base-100": "#171717",
"base-200": "#262626",
"base-300": "#404040",
"--glass-reflex-opacity": "0.001",
},
},
],
logs: false,
},
plugins: [
require("@tailwindcss/typography"),
require("@iconify/tailwind").addDynamicIconSelectors(),
require("daisyui"),
],
}