init
This commit is contained in:
23
.gitignore
vendored
Normal file
23
.gitignore
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
node_modules
|
||||
|
||||
# Output
|
||||
.output
|
||||
.vercel
|
||||
.netlify
|
||||
.wrangler
|
||||
/.svelte-kit
|
||||
/build
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Env
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.test
|
||||
|
||||
# Vite
|
||||
vite.config.js.timestamp-*
|
||||
vite.config.ts.timestamp-*
|
||||
9
.prettierignore
Normal file
9
.prettierignore
Normal file
@@ -0,0 +1,9 @@
|
||||
# Package Managers
|
||||
package-lock.json
|
||||
pnpm-lock.yaml
|
||||
yarn.lock
|
||||
bun.lock
|
||||
bun.lockb
|
||||
|
||||
# Miscellaneous
|
||||
/static/
|
||||
16
.prettierrc
Normal file
16
.prettierrc
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"useTabs": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"printWidth": 100,
|
||||
"plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": "*.svelte",
|
||||
"options": {
|
||||
"parser": "svelte"
|
||||
}
|
||||
}
|
||||
],
|
||||
"tailwindStylesheet": "./src/app.css"
|
||||
}
|
||||
38
README.md
Normal file
38
README.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# sv
|
||||
|
||||
Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli).
|
||||
|
||||
## Creating a project
|
||||
|
||||
If you're seeing this, you've probably already done this step. Congrats!
|
||||
|
||||
```sh
|
||||
# create a new project in the current directory
|
||||
npx sv create
|
||||
|
||||
# create a new project in my-app
|
||||
npx sv create my-app
|
||||
```
|
||||
|
||||
## Developing
|
||||
|
||||
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
|
||||
|
||||
```sh
|
||||
npm run dev
|
||||
|
||||
# or start the server and open the app in a new browser tab
|
||||
npm run dev -- --open
|
||||
```
|
||||
|
||||
## Building
|
||||
|
||||
To create a production version of your app:
|
||||
|
||||
```sh
|
||||
npm run build
|
||||
```
|
||||
|
||||
You can preview the production build with `npm run preview`.
|
||||
|
||||
> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment.
|
||||
42
package.json
Normal file
42
package.json
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "arch-repo",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"prepare": "svelte-kit sync || echo ''",
|
||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"format": "prettier --write .",
|
||||
"lint": "prettier --check ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-auto": "^6.1.0",
|
||||
"@sveltejs/kit": "^2.43.2",
|
||||
"@sveltejs/vite-plugin-svelte": "^6.2.0",
|
||||
"@tailwindcss/forms": "^0.5.10",
|
||||
"@tailwindcss/typography": "^0.5.18",
|
||||
"@tailwindcss/vite": "^4.1.13",
|
||||
"flowbite": "^3.1.2",
|
||||
"flowbite-svelte": "^1.18.0",
|
||||
"flowbite-svelte-icons": "^3.0.0",
|
||||
"prettier": "^3.6.2",
|
||||
"prettier-plugin-svelte": "^3.4.0",
|
||||
"prettier-plugin-tailwindcss": "^0.6.14",
|
||||
"svelte": "^5.39.5",
|
||||
"svelte-check": "^4.3.2",
|
||||
"tailwindcss": "^4.1.13",
|
||||
"typescript": "^5.9.2",
|
||||
"vite": "^7.1.7"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ts-stack/markdown": "^1.5.0",
|
||||
"monorepo": "github:ts-stack/markdown",
|
||||
"tailwind-merge": "^3.3.1",
|
||||
"uuid": "^13.0.0",
|
||||
"valibot": "^1.1.0"
|
||||
}
|
||||
}
|
||||
1787
pnpm-lock.yaml
generated
Normal file
1787
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
3
pnpm-workspace.yaml
Normal file
3
pnpm-workspace.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
onlyBuiltDependencies:
|
||||
- esbuild
|
||||
- '@tailwindcss/oxide'
|
||||
127
src/app.css
Normal file
127
src/app.css
Normal file
@@ -0,0 +1,127 @@
|
||||
@import 'tailwindcss';
|
||||
@plugin '@tailwindcss/forms';
|
||||
@plugin '@tailwindcss/typography';
|
||||
|
||||
@plugin 'flowbite/plugin';
|
||||
|
||||
@custom-variant dark (&:where(.dark, .dark *));
|
||||
|
||||
@theme {
|
||||
--color-primary-50: #fff5f2;
|
||||
--color-primary-100: #fff1ee;
|
||||
--color-primary-200: #ffe4de;
|
||||
--color-primary-300: #ffd5cc;
|
||||
--color-primary-400: #ffbcad;
|
||||
--color-primary-500: #fe795d;
|
||||
--color-primary-600: #ef562f;
|
||||
--color-primary-700: #eb4f27;
|
||||
--color-primary-800: #cc4522;
|
||||
--color-primary-900: #a5371b;
|
||||
|
||||
--color-secondary-50: #f0f9ff;
|
||||
--color-secondary-100: #e0f2fe;
|
||||
--color-secondary-200: #bae6fd;
|
||||
--color-secondary-300: #7dd3fc;
|
||||
--color-secondary-400: #38bdf8;
|
||||
--color-secondary-500: #0ea5e9;
|
||||
--color-secondary-600: #0284c7;
|
||||
--color-secondary-700: #0369a1;
|
||||
--color-secondary-800: #075985;
|
||||
--color-secondary-900: #0c4a6e;
|
||||
}
|
||||
|
||||
@source "../node_modules/flowbite-svelte/dist";
|
||||
@source "../node_modules/flowbite-svelte-icons/dist";
|
||||
|
||||
@layer base {
|
||||
/* disable chrome cancel button */
|
||||
input[type='search']::-webkit-search-cancel-button {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
/* Custom heading styles */
|
||||
@layer components {
|
||||
h1 {
|
||||
@apply mb-4 text-4xl font-bold text-gray-900;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@apply mb-3 text-3xl font-semibold text-gray-800;
|
||||
}
|
||||
|
||||
h3 {
|
||||
@apply mb-2 text-2xl font-medium text-gray-700;
|
||||
}
|
||||
|
||||
h4 {
|
||||
@apply mb-2 text-xl font-medium text-gray-600;
|
||||
}
|
||||
|
||||
h5 {
|
||||
@apply mb-1 text-lg font-medium text-gray-500;
|
||||
}
|
||||
|
||||
h6 {
|
||||
@apply mb-1 text-base font-medium text-gray-400;
|
||||
}
|
||||
}
|
||||
|
||||
@layer markdown {
|
||||
.app-markdown {
|
||||
h1 {
|
||||
@apply mt-8 mb-4 text-3xl font-bold;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@apply mt-6 mb-3 text-2xl font-semibold;
|
||||
}
|
||||
|
||||
h3 {
|
||||
@apply mt-5 mb-2 text-xl font-semibold;
|
||||
}
|
||||
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
@apply mt-4 mb-2 text-xl font-medium;
|
||||
}
|
||||
|
||||
a {
|
||||
@apply text-blue-600 underline transition-colors hover:text-blue-800;
|
||||
}
|
||||
|
||||
ul {
|
||||
@apply mb-4 list-disc pl-6;
|
||||
}
|
||||
ol {
|
||||
@apply mb-4 list-decimal pl-6;
|
||||
}
|
||||
|
||||
li {
|
||||
@apply mb-1;
|
||||
}
|
||||
|
||||
table {
|
||||
@apply my-6 w-full border-collapse shadow-sm;
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
@apply border border-gray-200 px-4 py-2 text-left;
|
||||
}
|
||||
|
||||
th {
|
||||
@apply font-semibold;
|
||||
}
|
||||
/**/
|
||||
/* tr:nth-child(even) { */
|
||||
/* @apply bg-gray-50; */
|
||||
/* } */
|
||||
blockquote {
|
||||
@apply rounded-md p-3 italic backdrop-blur-md;
|
||||
}
|
||||
p {
|
||||
@apply mb-2;
|
||||
}
|
||||
}
|
||||
}
|
||||
13
src/app.d.ts
vendored
Normal file
13
src/app.d.ts
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// See https://svelte.dev/docs/kit/types#app.d.ts
|
||||
// for information about these interfaces
|
||||
declare global {
|
||||
namespace App {
|
||||
// interface Error {}
|
||||
// interface Locals {}
|
||||
// interface PageData {}
|
||||
// interface PageState {}
|
||||
// interface Platform {}
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
11
src/app.html
Normal file
11
src/app.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
||||
42
src/lib/artefact.remote.ts
Normal file
42
src/lib/artefact.remote.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { command, form, query } from '$app/server';
|
||||
import * as v from 'valibot';
|
||||
import { artefacts, Status, type Artefact } from './artefact';
|
||||
import { error } from '@sveltejs/kit';
|
||||
|
||||
export const getArtefacts = query(
|
||||
v.object({
|
||||
view: v.string(),
|
||||
tags: v.array(v.string())
|
||||
}),
|
||||
({ view, tags }): Artefact[] => {
|
||||
const a = artefacts.filter(({ views }) => views.includes(view) || view === 'Projekt');
|
||||
if (tags.length == 0) return a;
|
||||
|
||||
return a.filter((a) => {
|
||||
for (const tag of a.tags) {
|
||||
if (tags.includes(tag)) return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
export const updateArtefact = form(
|
||||
v.object({
|
||||
uuid: v.string(),
|
||||
content: v.string(),
|
||||
status: v.enum(Status)
|
||||
}),
|
||||
(a) => {
|
||||
const i = artefacts.findIndex(({ uuid }) => uuid === a.uuid);
|
||||
if (i < 0) {
|
||||
return error(404, 'not exist');
|
||||
}
|
||||
|
||||
artefacts[i] = { ...artefacts[i], status: a.status, content: a.content };
|
||||
}
|
||||
);
|
||||
|
||||
export const getArtefact = query(v.string(), (uuid): Artefact | undefined => {
|
||||
return artefacts.find((a) => a.uuid === uuid);
|
||||
});
|
||||
316
src/lib/artefact.ts
Normal file
316
src/lib/artefact.ts
Normal file
@@ -0,0 +1,316 @@
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
export enum Status {
|
||||
Fehlt = 'Fehlt',
|
||||
InBearbeitung = 'In Bearbeitung',
|
||||
Erledigt = 'Erledigt',
|
||||
Geprueft = 'Geprüft'
|
||||
}
|
||||
|
||||
// Optional: recreate the array from the enum
|
||||
export const statusValues: { value: string; name: string }[] = Object.values(Status).map(
|
||||
(value) => ({
|
||||
value,
|
||||
name: value
|
||||
})
|
||||
);
|
||||
|
||||
export interface Artefact {
|
||||
uuid: string;
|
||||
tags: string[];
|
||||
views: string[];
|
||||
title: string;
|
||||
description: string;
|
||||
content: string; // link to document
|
||||
status: Status;
|
||||
}
|
||||
|
||||
export let artefacts: Artefact[] = [
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
title: 'Integration Service Desk',
|
||||
description:
|
||||
'Befähigen des 1-Level Supports. Beschreibt, wie der Service Desk in den Betrieb integriert wird (Schnittstellen, Prozesse, Eskalationswege)',
|
||||
views: ['Betrieb', 'Governance'],
|
||||
tags: ['layer:Application', 'type:Betrieb', 'hermes:Einführung'],
|
||||
content: '',
|
||||
status: Status.Fehlt
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
title: 'P042 - Informationssicherheits- und Datenschutzkonzept (ISDS)',
|
||||
description:
|
||||
'Zentrales Konzept zur Umsetzung der Informationssicherheit und des Datenschutzes im Projekt bzw. System',
|
||||
views: ['Sicherheit'],
|
||||
tags: ['layer:Business', 'type:Sicherheit', 'hermes:Konzept'],
|
||||
content: '',
|
||||
status: Status.Fehlt
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
title: 'BCM-Plan (Business Continuity Management)',
|
||||
description:
|
||||
'Beschreibt Massnahmen zur Aufrechterhaltung der kritischen Geschäftsprozesse bei Störungen',
|
||||
views: ['Sicherheit', 'Architektur'],
|
||||
tags: ['type:Sicherheit', 'type:Architektur'],
|
||||
content: '',
|
||||
status: Status.Fehlt
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
title: 'BHB',
|
||||
description: 'Betriebshandbücher',
|
||||
views: ['Betrieb', 'Architektur'],
|
||||
tags: ['type:Betrieb', 'type:Architektur'],
|
||||
content: '',
|
||||
status: Status.Fehlt
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
title: 'AKP-Architekturkonformitätsprüfung',
|
||||
description: 'Bewertet, ob ein System die Architekturleitlinien der Organisation erfüllt',
|
||||
views: ['Architektur'],
|
||||
tags: ['type:Architektur'],
|
||||
content: '',
|
||||
status: Status.Fehlt
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
title: 'AKP-Checkliste',
|
||||
description: 'Enthält Prüfpunkte und Bewertungskriterien für Architekturkonformität',
|
||||
views: ['Architektur'],
|
||||
tags: ['type:Architektur'],
|
||||
content: '',
|
||||
status: Status.Fehlt
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
title: 'SKP – Sicherheitskonformitätsprüfung',
|
||||
description: 'Prüft, ob Sicherheitsmassnahmen und ISB-Vorgaben eingehalten werden',
|
||||
views: ['Sicherheit'],
|
||||
tags: ['type:Sicherheit'],
|
||||
content: '',
|
||||
status: Status.Fehlt
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
title: 'SKP-Checkliste',
|
||||
description: 'Prüfliste mit Bewertungspunkten zur Sicherheitskonformität',
|
||||
views: ['Architektur'],
|
||||
tags: ['type:Architektur'],
|
||||
content: '',
|
||||
status: Status.Fehlt
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
title: 'Recovery-Plan',
|
||||
description:
|
||||
'Beschreibt Verfahren zur Wiederherstellung von IT-Systemen und Daten nach Ausfällen',
|
||||
views: ['Sicherheit', 'Betrieb'],
|
||||
tags: ['type:Sicherheit', 'type:Betrieb'],
|
||||
content: '',
|
||||
status: Status.Fehlt
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
title: 'Si001 - Hi01: Massnahmenumsetzung zum IT-Grundschutz in der BV',
|
||||
description:
|
||||
'Dokumentiert die Umsetzung der ISB-Sicherheitsmassnahmen gemäss IT-Grundschutzprofil',
|
||||
views: ['Sicherheit'],
|
||||
tags: ['type:Sicherheit'],
|
||||
content: '',
|
||||
status: Status.Fehlt
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
title: 'Architekturvision',
|
||||
description: 'Vermittelt Zielbild, Nutzen und Leitplanken für das Vorhaben',
|
||||
views: ['Management'],
|
||||
tags: ['type:Management'],
|
||||
content: '',
|
||||
status: Status.Fehlt
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
title: 'Systemkontext (arc42)',
|
||||
description: 'Zeigt externe Systeme, Schnittstellen und Abhängigkeiten',
|
||||
views: ['Architektur'],
|
||||
tags: ['type:Architektur'],
|
||||
content: '',
|
||||
status: Status.Fehlt
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
title: 'Stakeholderanalyse (arc42)',
|
||||
description: 'Identifiziert relevante Akteure, Rollen und Interessen',
|
||||
views: ['Architektur'],
|
||||
tags: ['type:Architektur'],
|
||||
content: '',
|
||||
status: Status.Fehlt
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
title: 'Architekturkonzept',
|
||||
description: 'Beschreibt Architekturentscheidungen, Aufbau und Integrationsprinzipien',
|
||||
views: ['Architektur'],
|
||||
tags: ['type:Architektur'],
|
||||
content: '',
|
||||
status: Status.Fehlt
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
title: 'Bausteinsicht (arc42)',
|
||||
description: 'Zeigt Systemkomponenten und deren Beziehungen',
|
||||
views: ['Architektur'],
|
||||
tags: ['type:Architektur'],
|
||||
content: '',
|
||||
status: Status.Fehlt
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
title: 'Laufzeitsicht (arc42)',
|
||||
description: 'Beschreibt Interaktionen und dynamisches Verhalten',
|
||||
views: ['Architektur'],
|
||||
tags: ['type:Architektur'],
|
||||
content: '',
|
||||
status: Status.Fehlt
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
title: 'Verteilungssicht (arc42)',
|
||||
description: 'Zeigt Deployments, Infrastruktur und Umgebungen',
|
||||
views: ['Architektur'],
|
||||
tags: ['type:Architektur'],
|
||||
content: '',
|
||||
status: Status.Fehlt
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
title: 'Architekturentscheidungen (ADR)',
|
||||
description: 'Dokumentiert wesentliche Architekturentscheidungen mit Begründungen',
|
||||
views: ['Architektur'],
|
||||
tags: ['type:Architektur'],
|
||||
content: '',
|
||||
status: Status.Fehlt
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
title: 'Datenmodell',
|
||||
description: 'Beschreibt zentrale Datenobjekte und Relationen',
|
||||
views: ['Daten'],
|
||||
tags: ['type:Daten'],
|
||||
content: '',
|
||||
status: Status.Fehlt
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
title: 'Schutzbedarfsanalyse (SchuBAN)',
|
||||
description: 'Schutzbedarfsanalyse: Definiert Schutzziele und Schutzbedarf gemäss ISB-Vorgaben',
|
||||
views: ['Sicherheit'],
|
||||
tags: ['type:Sicherheit'],
|
||||
content: '',
|
||||
status: Status.Fehlt
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
title: 'Datenschutzkonzept',
|
||||
description: 'Beschreibt Umsetzung von DSG/DSV-Anforderungen',
|
||||
views: ['Sicherheit'],
|
||||
tags: ['type:Sicherheit'],
|
||||
content: '',
|
||||
status: Status.Fehlt
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
title: 'Sicherheitskonzept',
|
||||
description: 'Beschreibt Sicherheitsmassnahmen auf technischer & organisatorischer Ebene',
|
||||
views: ['Sicherheit'],
|
||||
tags: ['type:Sicherheit'],
|
||||
content: '',
|
||||
status: Status.Fehlt
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
title: 'Risikomanagement / Risikoanalyse',
|
||||
description: 'Erfasst Bedrohungen, Eintrittswahrscheinlichkeiten und Massnahmen',
|
||||
views: ['Sicherheit'],
|
||||
tags: ['type:Sicherheit'],
|
||||
content: '',
|
||||
status: Status.Fehlt
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
title: 'Zugriffs- & Berechtigungskonzept',
|
||||
description: 'Definiert Rollen, Rechte und Zugriffsebenen',
|
||||
views: ['Sicherheit'],
|
||||
tags: ['type:Sicherheit'],
|
||||
content: '',
|
||||
status: Status.Fehlt
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
title: 'IKT-Minimalstandard-Nachweis',
|
||||
description: 'Belegt die Einhaltung der Minimalstandard-Kontrollen',
|
||||
views: ['Sicherheit'],
|
||||
tags: ['type:Sicherheit'],
|
||||
content: '',
|
||||
status: Status.Fehlt
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
title: 'Traceability-Matrix',
|
||||
description: 'Verknüpft Anforderungen mit Architektur- und Sicherheitsmassnahmen',
|
||||
views: ['Governance'],
|
||||
tags: ['type:Governance'],
|
||||
content: '',
|
||||
status: Status.Fehlt
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
title: 'Versionierungsübersicht',
|
||||
description: 'Hält aktuelle und historische Versionen der Artefakte fest',
|
||||
views: ['Governance'],
|
||||
tags: ['type:Governance'],
|
||||
content: '',
|
||||
status: Status.Fehlt
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
title: 'Architektur-Review / Freigabeprotokoll',
|
||||
description: 'Dokumentiert Review-Resultate und formelle Freigabe',
|
||||
views: ['Governance'],
|
||||
tags: ['type:Governance'],
|
||||
content: '',
|
||||
status: Status.Fehlt
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
title: 'Qualitätsanforderungen (arc42)',
|
||||
description: 'Definiert nicht-funktionale Anforderungen (z. B. Performance, Skalierbarkeit)',
|
||||
views: ['Architektur'],
|
||||
tags: ['type:Architektur'],
|
||||
content: '',
|
||||
status: Status.Fehlt
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
title: 'Monitoring- & Betriebsarchitektur',
|
||||
description: 'Beschreibt Überwachung, Logging, Service KPIs',
|
||||
views: ['Betrieb'],
|
||||
tags: ['type:Betrieb'],
|
||||
content: '',
|
||||
status: Status.Fehlt
|
||||
},
|
||||
{
|
||||
uuid: uuidv4(),
|
||||
title: 'Glossar (arc42)',
|
||||
description: 'Vereinheitlicht Begriffe für alle Artefakte',
|
||||
views: ['Governance', 'Sicherheit', 'Architektur', 'Daten'],
|
||||
tags: ['type:Governance', 'type:Sicherheit', 'type:Architektur', 'type:Daten'],
|
||||
content: '',
|
||||
status: Status.Fehlt
|
||||
}
|
||||
];
|
||||
export const views: string[] = [
|
||||
...Array.from(new Set(artefacts.flatMap((a) => a.views))),
|
||||
'Projekt'
|
||||
];
|
||||
1
src/lib/assets/favicon.svg
Normal file
1
src/lib/assets/favicon.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="107" height="128" viewBox="0 0 107 128"><title>svelte-logo</title><path d="M94.157 22.819c-10.4-14.885-30.94-19.297-45.792-9.835L22.282 29.608A29.92 29.92 0 0 0 8.764 49.65a31.5 31.5 0 0 0 3.108 20.231 30 30 0 0 0-4.477 11.183 31.9 31.9 0 0 0 5.448 24.116c10.402 14.887 30.942 19.297 45.791 9.835l26.083-16.624A29.92 29.92 0 0 0 98.235 78.35a31.53 31.53 0 0 0-3.105-20.232 30 30 0 0 0 4.474-11.182 31.88 31.88 0 0 0-5.447-24.116" style="fill:#ff3e00"/><path d="M45.817 106.582a20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.503 18 18 0 0 1 .624-2.435l.49-1.498 1.337.981a33.6 33.6 0 0 0 10.203 5.098l.97.294-.09.968a5.85 5.85 0 0 0 1.052 3.878 6.24 6.24 0 0 0 6.695 2.485 5.8 5.8 0 0 0 1.603-.704L69.27 76.28a5.43 5.43 0 0 0 2.45-3.631 5.8 5.8 0 0 0-.987-4.371 6.24 6.24 0 0 0-6.698-2.487 5.7 5.7 0 0 0-1.6.704l-9.953 6.345a19 19 0 0 1-5.296 2.326 20.72 20.72 0 0 1-22.237-8.243 19.17 19.17 0 0 1-3.277-14.502 17.99 17.99 0 0 1 8.13-12.052l26.081-16.623a19 19 0 0 1 5.3-2.329 20.72 20.72 0 0 1 22.237 8.243 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-.624 2.435l-.49 1.498-1.337-.98a33.6 33.6 0 0 0-10.203-5.1l-.97-.294.09-.968a5.86 5.86 0 0 0-1.052-3.878 6.24 6.24 0 0 0-6.696-2.485 5.8 5.8 0 0 0-1.602.704L37.73 51.72a5.42 5.42 0 0 0-2.449 3.63 5.79 5.79 0 0 0 .986 4.372 6.24 6.24 0 0 0 6.698 2.486 5.8 5.8 0 0 0 1.602-.704l9.952-6.342a19 19 0 0 1 5.295-2.328 20.72 20.72 0 0 1 22.237 8.242 19.17 19.17 0 0 1 3.277 14.503 18 18 0 0 1-8.13 12.053l-26.081 16.622a19 19 0 0 1-5.3 2.328" style="fill:#fff"/></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
97
src/lib/components/Artefact.svelte
Normal file
97
src/lib/components/Artefact.svelte
Normal file
@@ -0,0 +1,97 @@
|
||||
<script lang="ts">
|
||||
import { statusValues, type Artefact, Status } from '$lib/artefact';
|
||||
import { Button, Card, Label, Modal, Select } from 'flowbite-svelte';
|
||||
import MarkdownEditor from './MarkdownEditor.svelte';
|
||||
import { updateArtefact } from '$lib/artefact.remote';
|
||||
let { artefact, tagClick = (_) => {} }: { artefact: Artefact; tagClick?: (tag: string) => void } =
|
||||
$props();
|
||||
|
||||
let edit = $state(false);
|
||||
let editValue: Artefact = $state({
|
||||
uuid: '',
|
||||
tags: [],
|
||||
views: [],
|
||||
title: '',
|
||||
description: '',
|
||||
content: '',
|
||||
status: Status.Fehlt
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
editValue = artefact;
|
||||
});
|
||||
|
||||
function getStatusColor(status: Status): string {
|
||||
switch (status) {
|
||||
case 'Fehlt':
|
||||
return 'bg-red-500'; // Missing
|
||||
case 'In Bearbeitung':
|
||||
return 'bg-yellow-400'; // In progress
|
||||
case 'Erledigt':
|
||||
return 'bg-green-500'; // Done
|
||||
case 'Geprüft':
|
||||
return 'bg-green-800'; // Checked
|
||||
default:
|
||||
return 'bg-red-500'; // Fallback
|
||||
}
|
||||
}
|
||||
function tagParts(tag: string): string[] {
|
||||
const parts = tag.split(':');
|
||||
if (parts.length < 2) {
|
||||
return ['', tag];
|
||||
}
|
||||
return [parts[0], parts[1]];
|
||||
}
|
||||
</script>
|
||||
|
||||
<Card class="grid grid-cols-2 gap-5 p-5">
|
||||
<h1 class="col-span-2">{artefact.title}</h1>
|
||||
<div class="col-span-2 flex flex-wrap">
|
||||
{#each artefact.tags as tag}
|
||||
<button
|
||||
onclick={() => tagClick(tag)}
|
||||
class="m-1 flex h-8 items-center overflow-hidden rounded-2xl border-2 border-amber-600 bg-amber-200 text-sm transition hover:bg-amber-300"
|
||||
>
|
||||
{#if tagParts(tag)[0] !== ''}
|
||||
<span
|
||||
class="flex items-center rounded-l-2xl bg-amber-500 px-3 py-1 font-medium text-amber-900"
|
||||
>
|
||||
{tagParts(tag)[0].toUpperCase()}:
|
||||
</span>
|
||||
{/if}
|
||||
<span class="px-3 py-1 text-amber-900">{tagParts(tag)[1]}</span>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
<p class="col-span-2 my-5">{artefact.description}</p>
|
||||
|
||||
<p
|
||||
class={`flex items-center justify-center rounded-3xl p-2 text-white ${getStatusColor(artefact.status)}`}
|
||||
>
|
||||
{artefact.status}
|
||||
</p>
|
||||
<Button onclick={() => (edit = true)}>Edit</Button>
|
||||
</Card>
|
||||
|
||||
<Modal bind:open={edit}>
|
||||
<form
|
||||
{...updateArtefact}
|
||||
onsubmit={() => {
|
||||
edit = false;
|
||||
}}
|
||||
class="m-8"
|
||||
>
|
||||
<input type="hidden" value={editValue.uuid} name="uuid" />
|
||||
<input type="hidden" value={editValue.content} name="content" />
|
||||
<div class="my-5">
|
||||
<Label>Status</Label>
|
||||
<Select bind:value={editValue.status} items={statusValues} name="status"></Select>
|
||||
</div>
|
||||
<div class="my-5">
|
||||
<Label>content</Label>
|
||||
<MarkdownEditor bind:value={editValue.content} />
|
||||
</div>
|
||||
|
||||
<Button type="submit">Save</Button>
|
||||
</form>
|
||||
</Modal>
|
||||
38
src/lib/components/ArtefactsView.svelte
Normal file
38
src/lib/components/ArtefactsView.svelte
Normal file
@@ -0,0 +1,38 @@
|
||||
<script lang="ts">
|
||||
import { getArtefacts } from '$lib/artefact.remote';
|
||||
import Artefact from '$lib/components/Artefact.svelte';
|
||||
import { Button, Input } from 'flowbite-svelte';
|
||||
import { TrashBinOutline } from 'flowbite-svelte-icons';
|
||||
|
||||
let { view }: { view: string } = $props();
|
||||
|
||||
let tags: string[] = $state([]);
|
||||
let newTag = $state('');
|
||||
</script>
|
||||
|
||||
<div class="m-5 grid grid-cols-5 gap-5">
|
||||
{#each tags as tag}
|
||||
<p class="col-span-4">{tag}</p>
|
||||
<Button
|
||||
color="red"
|
||||
onclick={() => {
|
||||
tags = tags.filter((t) => t != tag);
|
||||
}}><TrashBinOutline /></Button
|
||||
>
|
||||
{/each}
|
||||
<Input class="col-span-4" bind:value={newTag}></Input>
|
||||
<Button
|
||||
onclick={() => {
|
||||
tags.push(newTag);
|
||||
newTag = '';
|
||||
}}>+</Button
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{#await getArtefacts({ view, tags }) then artefacts}
|
||||
{#each artefacts as artefact (artefact.uuid)}
|
||||
<Artefact {artefact} tagClick={(tag) => tags.push(tag)} />
|
||||
{/each}
|
||||
{/await}
|
||||
</div>
|
||||
46
src/lib/components/Markdown.svelte
Normal file
46
src/lib/components/Markdown.svelte
Normal file
@@ -0,0 +1,46 @@
|
||||
<script lang="ts">
|
||||
import { Marked, Renderer } from '@ts-stack/markdown';
|
||||
import { type ClassNameValue, twMerge } from 'tailwind-merge';
|
||||
|
||||
Marked.setOptions({
|
||||
renderer: new Renderer(),
|
||||
gfm: true,
|
||||
tables: true,
|
||||
breaks: false,
|
||||
pedantic: false,
|
||||
sanitize: false,
|
||||
smartLists: true,
|
||||
smartypants: false
|
||||
});
|
||||
let { src, class: className = '' }: { src: string; class?: ClassNameValue } = $props();
|
||||
|
||||
let content: Element[] = $state([]);
|
||||
const DOM_PARSER = new DOMParser();
|
||||
|
||||
$effect(() => {
|
||||
const s = src;
|
||||
const parsedMarkdown = DOM_PARSER.parseFromString(Marked.parse(s), 'text/html');
|
||||
content = [...parsedMarkdown.body.children];
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class={twMerge('app-markdown', className)}>
|
||||
{#if content != null}
|
||||
{#each content as element (element)}
|
||||
<article>
|
||||
{#if element.firstChild != null && element.firstChild.nodeName == 'IMG'}
|
||||
<img
|
||||
src={(element.firstChild as HTMLImageElement).src}
|
||||
alt={(element.firstChild as HTMLImageElement).alt}
|
||||
/>
|
||||
{:else}
|
||||
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
||||
{@html element.outerHTML}
|
||||
{/if}
|
||||
</article>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
25
src/lib/components/MarkdownEditor.svelte
Normal file
25
src/lib/components/MarkdownEditor.svelte
Normal file
@@ -0,0 +1,25 @@
|
||||
<script lang="ts">
|
||||
import Markdown from './Markdown.svelte';
|
||||
|
||||
let {
|
||||
value = $bindable(),
|
||||
name
|
||||
}: { value: string; onsave?: () => Promise<void>; name?: string } = $props();
|
||||
|
||||
let area: HTMLTextAreaElement = $state()!;
|
||||
</script>
|
||||
|
||||
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
|
||||
<div
|
||||
class="relative min-h-40 w-full rounded-lg border-0 bg-gray-50 text-sm focus:border-1 focus:ring-primary-500 disabled:cursor-not-allowed disabled:opacity-50 dark:bg-gray-700 dark:focus:ring-primary-500"
|
||||
>
|
||||
<textarea
|
||||
bind:value
|
||||
bind:this={area}
|
||||
{name}
|
||||
class="min-h-full w-full rounded-lg border-0 bg-gray-50 p-2.5 dark:bg-gray-700"
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<Markdown src={value} class="overflow-scroll" />
|
||||
</div>
|
||||
1
src/lib/index.ts
Normal file
1
src/lib/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
// place files you want to import through the `$lib` alias in this folder.
|
||||
26
src/routes/+layout.svelte
Normal file
26
src/routes/+layout.svelte
Normal file
@@ -0,0 +1,26 @@
|
||||
<script lang="ts">
|
||||
import '../app.css';
|
||||
import { Navbar, NavBrand, NavLi, NavUl, NavHamburger } from 'flowbite-svelte';
|
||||
let { children } = $props();
|
||||
</script>
|
||||
|
||||
<svelte:head></svelte:head>
|
||||
|
||||
<!-- <Navbar> -->
|
||||
<!-- <NavBrand href="/"> -->
|
||||
<!-- <span class="self-center text-xl font-semibold whitespace-nowrap dark:text-white">ArchRepo</span -->
|
||||
<!-- > -->
|
||||
<!-- </NavBrand> -->
|
||||
<!-- <NavHamburger /> -->
|
||||
<!-- <NavUl> -->
|
||||
<!-- <NavLi href="/">Home</NavLi> -->
|
||||
<!-- <NavLi href="/about">About</NavLi> -->
|
||||
<!-- <NavLi href="/docs/components/navbar">Navbar</NavLi> -->
|
||||
<!-- <NavLi href="/pricing">Pricing</NavLi> -->
|
||||
<!-- <NavLi href="/contact">Contact</NavLi> -->
|
||||
<!-- </NavUl> -->
|
||||
<!-- </Navbar> -->
|
||||
|
||||
<main class="mx-10 mt-5 xl:mx-44">
|
||||
{@render children?.()}
|
||||
</main>
|
||||
15
src/routes/+page.svelte
Normal file
15
src/routes/+page.svelte
Normal file
@@ -0,0 +1,15 @@
|
||||
<script lang="ts">
|
||||
import { views } from '$lib/artefact';
|
||||
import ArtefactsView from '$lib/components/ArtefactsView.svelte';
|
||||
import { Tabs, TabItem } from 'flowbite-svelte';
|
||||
</script>
|
||||
|
||||
<h1>Project Overview: TODO app</h1>
|
||||
|
||||
<Tabs>
|
||||
{#each views as view, id}
|
||||
<TabItem open={id === 1} title={view}>
|
||||
<ArtefactsView {view} />
|
||||
</TabItem>
|
||||
{/each}
|
||||
</Tabs>
|
||||
3
static/robots.txt
Normal file
3
static/robots.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
# allow crawling everything by default
|
||||
User-agent: *
|
||||
Disallow:
|
||||
23
svelte.config.js
Normal file
23
svelte.config.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import adapter from '@sveltejs/adapter-auto';
|
||||
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
// Consult https://svelte.dev/docs/kit/integrations
|
||||
// for more information about preprocessors
|
||||
preprocess: vitePreprocess(),
|
||||
|
||||
kit: {
|
||||
experimental: {
|
||||
remoteFunctions: true
|
||||
},
|
||||
adapter: adapter()
|
||||
},
|
||||
compilerOptions: {
|
||||
experimental: {
|
||||
async: true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default config;
|
||||
19
tsconfig.json
Normal file
19
tsconfig.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"extends": "./.svelte-kit/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"moduleResolution": "bundler"
|
||||
}
|
||||
// Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
|
||||
// except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
|
||||
//
|
||||
// To make changes to top-level options such as include and exclude, we recommend extending
|
||||
// the generated config; see https://svelte.dev/docs/kit/configuration#typescript
|
||||
}
|
||||
7
vite.config.ts
Normal file
7
vite.config.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
import { sveltekit } from '@sveltejs/kit/vite';
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [tailwindcss(), sveltekit()]
|
||||
});
|
||||
Reference in New Issue
Block a user