making my way downtown~
This commit is contained in:
parent
2e73e69bf1
commit
db3b1e6a11
44
Dockerfile
Normal file
44
Dockerfile
Normal file
@ -0,0 +1,44 @@
|
||||
# syntax = docker/dockerfile:1
|
||||
|
||||
# Adjust NODE_VERSION as desired
|
||||
ARG NODE_VERSION=21.7.3
|
||||
FROM node:${NODE_VERSION}-slim as base
|
||||
|
||||
LABEL fly_launch_runtime="Node.js"
|
||||
|
||||
# Node.js app lives here
|
||||
WORKDIR /app
|
||||
|
||||
# Set production environment
|
||||
ENV NODE_ENV="production"
|
||||
|
||||
|
||||
# Throw-away build stage to reduce size of final image
|
||||
FROM base as build
|
||||
|
||||
# Install packages needed to build node modules
|
||||
RUN apt-get update -qq && \
|
||||
apt-get install --no-install-recommends -y build-essential node-gyp pkg-config python-is-python3
|
||||
|
||||
# Install node modules
|
||||
COPY --link package-lock.json package.json ./
|
||||
RUN npm ci
|
||||
|
||||
# Copy application code
|
||||
COPY --link . .
|
||||
|
||||
|
||||
# Final stage for app image
|
||||
FROM base
|
||||
|
||||
# Copy built application
|
||||
COPY --from=build /app /app
|
||||
|
||||
# Setup sqlite3 on a separate volume
|
||||
RUN mkdir -p /data
|
||||
VOLUME /data
|
||||
|
||||
# Start the server by default, this can be overwritten at runtime
|
||||
EXPOSE 3000
|
||||
ENV DATABASE_URL="file:///data/sqlite.db"
|
||||
CMD [ "node", "index.js" ]
|
||||
@ -39,6 +39,7 @@
|
||||
nodePackages.typescript-language-server
|
||||
pgformatter
|
||||
hurl
|
||||
flyctl
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
24
fly.toml
Normal file
24
fly.toml
Normal file
@ -0,0 +1,24 @@
|
||||
# fly.toml app configuration file generated for unibackend on 2024-05-29T21:35:15+02:00
|
||||
#
|
||||
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
|
||||
#
|
||||
|
||||
app = 'unibackend'
|
||||
primary_region = 'ams'
|
||||
|
||||
[build]
|
||||
|
||||
[[mounts]]
|
||||
source = 'data'
|
||||
destination = '/data'
|
||||
|
||||
[http_service]
|
||||
internal_port = 3000
|
||||
force_https = true
|
||||
auto_stop_machines = true
|
||||
auto_start_machines = true
|
||||
min_machines_running = 0
|
||||
processes = ['app']
|
||||
|
||||
[[vm]]
|
||||
size = 'shared-cpu-1x'
|
||||
71
frontend/README.md
Normal file
71
frontend/README.md
Normal file
@ -0,0 +1,71 @@
|
||||
# Frontend
|
||||
|
||||
|
||||
Es soll eine Webseite mit HTML, CSS und JavaScript erstellt werden. Die Webseite soll hierbei eine Finanzverwaltung mit doppelter Buchführung abbilden.
|
||||
|
||||
**Anforderungen:**
|
||||
|
||||
- **Inhalt:**
|
||||
- Es sind zwei Unterseiten vorzusehen:
|
||||
- Übersicht über alle Konten
|
||||
- Liste der Buchungen eines Kontos
|
||||
- Die Übersichtsseite enthält je Konto mindestens folgende Angaben:
|
||||
- Name des Kontos
|
||||
- Beschreibung des Kontos
|
||||
- Kontonummer
|
||||
- Aktuelle Summe
|
||||
|
||||
Die Konten sollen hierbei hierarchisch angeordnet werden (beispielsweise Aufwendungen mit den Unterkonten Miete, Lebensmittel und Semesterbeitrag).
|
||||
Die Übersicht soll nach Kontonummer sortiert sein.
|
||||
|
||||
- - Die Buchungsliste für ein Konto enthält je Buchung mindestens folgende Angaben:
|
||||
- Titel der Buchung
|
||||
- Betrag der Buchung auf dem Konto
|
||||
- Gegenkonto
|
||||
- **Navigation:**
|
||||
- Zwischen Übersichtsseite und Buchungslisten soll eine Navigation über Hyperlinks möglich sein.
|
||||
- **Struktur:**
|
||||
- Es gibt auf allen Seiten einen einheitlichen Header und Footer.
|
||||
- **Stylesheets:**
|
||||
- CSS-Stylesheets sollen als separate Datei eingebunden werden
|
||||
- Für das Styling des Headers und Footers ist ein gemeinsames Stylesheet zu nutzen.
|
||||
- Die aktuelle Summe ist grün bei positiven, rot bei negativen und schwarz beim Betrag null zu formatieren.
|
||||
- Es werden keine CSS-Frameworks eingesetzt (Bootstrap o.Ä.)
|
||||
- Die Darstellung auf Smartphones und Desktop-Geräten soll sich unterscheiden (Responsive Design).
|
||||
- **Schnittstelle:**
|
||||
- Für den Abruf der Daten soll die bereitgestellte API genutzt werden.
|
||||
- Unter der URL, die ihr im ersten Labor erhaltet, sind die Endpunkte der API mit OpenAPI dokumentiert und können dort auch getestet werden.
|
||||
- Liefert die Schnittstelle einen Fehler zurück, soll dieser angemessen behandelt werden (d.h. Anzeige einer für den Benutzer sichtbaren und verständlichen Fehlermeldung).
|
||||
- **Bearbeitung:**
|
||||
- Buchungen sollen erstellt, bearbeitet und gelöscht werden können.
|
||||
- Konten sollen erstellt, bearbeitet und gelöscht werden können.
|
||||
- **Mehrteilige Buchungen:**
|
||||
- Buchungen sollen auch so erstellt werden können, dass in einer
|
||||
Buchung mehr als zwei Konten aufgeführt sind.
|
||||
- **Gesamtbuchungsübersicht:**
|
||||
- Es ist eine zusätzliche Seite zu erstellen, welche die Buchungen
|
||||
aller Konten enthält.
|
||||
- **Sortieren und Filtern:**
|
||||
- Auf der Buchungsübersicht und der Gesamtbuchungsübersicht ist eine Sortier- und Filterfunktion zu implementieren.
|
||||
- Die Sortierung muss auf- und absteigend erfolgen können nach:
|
||||
- Buchungsdatum
|
||||
- Wertstellungsdatum
|
||||
- Die Filterung muss erfolgen können nach:
|
||||
- Datum oder Zeitraum (Datum von - bis) für
|
||||
- Buchungsdatum
|
||||
- Wertstellungsdatum
|
||||
- Titel der Buchung
|
||||
- case-insensitive (Groß-/Kleinschreibung ignorieren)
|
||||
- auch Teilausdrücke finden (beispielsweise soll "Brot" den Eintrag "Abendbrot für Sonntag" finden)
|
||||
- Der Filter soll direkt beim Tippen angewandt werden, ohne dass eine Bestätigung notwendig ist.
|
||||
- Betrag der Buchung (größer als .../kleiner als ...)
|
||||
|
||||
Sind mehrere Filter gleichzeitig aktiv, werden diese logisch und-verknüpft.
|
||||
|
||||
- - Standardmäßig sollen nur Buchungen des laufenden Kalenderjahres absteigend sortiert nach Buchungsdatum angezeigt werden.
|
||||
- **Paginierung:**
|
||||
- Die Anzeige auf der Buchungsübersicht und der Gesamtbuchungsübersicht soll paginierbar sein, das heißt, es werden auf einer Seite nur eine bestimmte Menge (initial 10) an Buchungen angezeigt.
|
||||
- Die Seitengröße soll hierbei auf 10, 20, 50, 100 und „Alle“ einstellbar sein.
|
||||
- Sind weniger als 10 Einträge vorhanden, sollen die Einstellungen zur Paginierung (Seitenauswahl, Seitengröße) verborgen werden.
|
||||
- **Echtzeitupdates:**
|
||||
- Die Anwendung soll Änderungen bei den Buchungen über SSE empfangen und in Echtzeit anzeigen. Hierfür dient der Endpunkt /live des Backends.
|
||||
BIN
frontend/assets/favicon.ico
Normal file
BIN
frontend/assets/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.2 KiB |
66
frontend/html/account.html
Normal file
66
frontend/html/account.html
Normal file
@ -0,0 +1,66 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="description" content="Financial Accounting Tool Project" />
|
||||
<meta
|
||||
name="author"
|
||||
content="Maximilian Ruhm, Tarek Dafay, Christoph van Deest"
|
||||
/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Financial Accounting Tool</title>
|
||||
<link rel="icon" href="assets/favicon.ico" />
|
||||
<link rel="stylesheet" href="../style/index.css" />
|
||||
<link rel="stylesheet" href="../style/account.css" />
|
||||
<script type="module" src="../js/account.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this Website.</noscript>
|
||||
<div id="root">
|
||||
<div class="header">
|
||||
<span class="title" onclick="window.location.href='../index.html'"
|
||||
>Account-Editor</span
|
||||
><span class="nav" onclick="window.location.href='overview.html'"
|
||||
>Gesamtübersicht</span
|
||||
>
|
||||
</div>
|
||||
<div id="creation-container" class="center">
|
||||
<div class="center form-data">
|
||||
<div class="form-data-label">Kontonummer:</div>
|
||||
<input class="form-data-element" type="text" id="number" />
|
||||
</div>
|
||||
<div class="center form-data">
|
||||
<div class="form-data-label">Kontoname:</div>
|
||||
<input class="form-data-element" type="text" id="name" />
|
||||
</div>
|
||||
<div class="center form-data">
|
||||
<div class="form-data-label">Beschreibung:</div>
|
||||
<input class="form-data-element" type="text" id="description" />
|
||||
</div>
|
||||
<div class="center form-data">
|
||||
<div class="form-data-label">Kontoart:</div>
|
||||
<select class="form-data-element" id="type">
|
||||
<option disabled selected>--- Bitte Option wählen ---</option>
|
||||
<option>default</option>
|
||||
<option>meta</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="center form-data">
|
||||
<div class="form-data-label">Übergeordnetes Konto:</div>
|
||||
<select class="form-data-element" id="account-select">
|
||||
<option disabled selected>--- Wurzel Konto ---</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="save-buttons center">
|
||||
<input class="save-button" type="button" value="abbrechen" id="cancel" />
|
||||
<input class="save-button" type="button" value="löschen" id="delete" style="display: none;"/>
|
||||
<input class="save-button" type="button" value="erstellen" id="submit" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
Entwicklung des Frontends - Maximilian Ruhm, Tarek Dafay, Christoph van
|
||||
Deest
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
107
frontend/html/detail.html
Normal file
107
frontend/html/detail.html
Normal file
@ -0,0 +1,107 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="description" content="Financial Accounting Tool Project" />
|
||||
<meta
|
||||
name="author"
|
||||
content="Maximilian Ruhm, Tarek Dafay, Christoph van Deest"
|
||||
/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Financial Accounting Tool</title>
|
||||
<link rel="icon" href="assets/favicon.ico" />
|
||||
<link rel="stylesheet" href="../style/index.css" />
|
||||
<link rel="stylesheet" href="../style/detail.css" />
|
||||
<script type="module" src="../js/detail.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this Website.</noscript>
|
||||
<div id="root">
|
||||
<div class="header">
|
||||
<span class="title" onclick="window.location.href='../index.html'"
|
||||
>Detailansicht</span
|
||||
><span class="nav" onclick="window.location.href='overview.html'"
|
||||
>Gesamtübersicht</span
|
||||
>
|
||||
</div>
|
||||
<div class="link-container">
|
||||
<a href="../index.html">zurück zu Startseite</a>
|
||||
</div>
|
||||
<div id="detail-container"></div>
|
||||
<div id="table-container" style="display: flex">
|
||||
<div class="filter">
|
||||
<div class="filter-div">
|
||||
<span class="filter-text">Zeitraum von</span>
|
||||
<input type="date" id="beginDate" name="dateFilter" />
|
||||
<span class="filter-text">bis</span>
|
||||
<input type="date" id="endDate" name="dateFilter" />
|
||||
<input type="button" id="resetDates" value="X" />
|
||||
</div>
|
||||
<div class="center filter-div">
|
||||
<input
|
||||
class="search-text"
|
||||
id="searchText"
|
||||
name="textFilter"
|
||||
type="text"
|
||||
placeholder="Suche...."
|
||||
/>
|
||||
</div>
|
||||
<div class="center filter-div">
|
||||
<input
|
||||
class="amount-text"
|
||||
type="text"
|
||||
placeholder="Betrag ab...."
|
||||
id="beginAmount"
|
||||
name="textFilter"
|
||||
/>
|
||||
<input
|
||||
class="amount-text"
|
||||
type="text"
|
||||
placeholder="Betrag bis...."
|
||||
id="endAmount"
|
||||
name="textFilter"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table">
|
||||
<table class="entry-table">
|
||||
<thead>
|
||||
<tr class="entry-table-tr">
|
||||
<th class="entry-table-th clickable" id="bookingDate" width="15%">
|
||||
Buchungsdatum
|
||||
</th>
|
||||
<th class="entry-table-th clickable" id="valuta" width="15%">Valuta</th>
|
||||
<th class="entry-table-th" width="20%">Titel</th>
|
||||
<th class="entry-table-th" width="20%">Gegenkonto</th>
|
||||
<th class="entry-table-th" width="20%">Betrag</th>
|
||||
<th class="entry-table-th" width="10%" style="text-align: center;"><input type="button" class="action-button" value="+" id="add-transaction" /></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div id="pagination-div" class="pagination">
|
||||
<div class="pagination-part center">
|
||||
<input type="button" id="backward-page" class="page-button" value="Vorherige" />
|
||||
<input type="button" id="forward-page" class="page-button" value="Nächste" />
|
||||
</div>
|
||||
<div class="pagination-part center"><span id="page-info">1</span></div>
|
||||
<div class="pagination-part center">
|
||||
<span>Buchungen pro Seite:</span>
|
||||
<select id="table-range" class="dropdown">
|
||||
<option>10</option>
|
||||
<option>20</option>
|
||||
<option>50</option>
|
||||
<option>100</option>
|
||||
<option>Alle</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
Entwicklung des Frontends - Maximilian Ruhm, Tarek Dafay, Christoph van
|
||||
Deest
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
85
frontend/html/overview.html
Normal file
85
frontend/html/overview.html
Normal file
@ -0,0 +1,85 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="description" content="Financial Accounting Tool Project" />
|
||||
<meta
|
||||
name="author"
|
||||
content="Maximilian Ruhm, Tarek Dafay, Christoph van Deest"
|
||||
/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Financial Accounting Tool</title>
|
||||
<link rel="icon" href="assets/favicon.ico" />
|
||||
<link rel="stylesheet" href="../style/index.css" />
|
||||
<link rel="stylesheet" href="../style/detail.css" />
|
||||
<script type="module" src="../js/overview.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this Website.</noscript>
|
||||
<div id="root">
|
||||
<div class="header">
|
||||
<span class="title" onclick="window.location.href='../index.html'"
|
||||
>Buchhaltungs-Gesamtübersicht</span
|
||||
>
|
||||
</div>
|
||||
<div class="link-container">
|
||||
<a href="../index.html">zurück zu Startseite</a>
|
||||
</div>
|
||||
<div id="table-container" style="display: flex">
|
||||
<div class="filter">
|
||||
<div class="filter-div">
|
||||
<span class="filter-text">Zeitraum von</span>
|
||||
<input type="date" id="beginDate" name="dateFilter" />
|
||||
<span class="filter-text">bis</span>
|
||||
<input type="date" id="endDate" name="dateFilter" />
|
||||
<input type="button" id="resetDates" value="X" />
|
||||
</div>
|
||||
<div class="center filter-div">
|
||||
<input
|
||||
class="search-text"
|
||||
id="searchText"
|
||||
name="textFilter"
|
||||
type="text"
|
||||
placeholder="Suche...."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table">
|
||||
<table class="entry-table">
|
||||
<thead>
|
||||
<tr class="entry-table-tr">
|
||||
<th class="entry-table-th clickable" id="bookingDate">
|
||||
Buchungsdatum
|
||||
</th>
|
||||
<th class="entry-table-th clickable" id="valuta">Valuta</th>
|
||||
<th class="entry-table-th">Titel</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div id="pagination-div" class="pagination">
|
||||
<div class="pagination-part center">
|
||||
<input type="button" id="backward-page" class="page-button" value="Vorherige" />
|
||||
<input type="button" id="forward-page" class="page-button" value="Nächste" />
|
||||
</div>
|
||||
<div class="pagination-part center"><span id="page-info">1</span></div>
|
||||
<div class="pagination-part center">
|
||||
<span>Buchungen pro Seite:</span>
|
||||
<select id="table-range" class="dropdown">
|
||||
<option>10</option>
|
||||
<option>20</option>
|
||||
<option>50</option>
|
||||
<option>100</option>
|
||||
<option>Alle</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
Entwicklung des Frontends - Maximilian Ruhm, Tarek Dafay, Christoph van
|
||||
Deest
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
72
frontend/html/transaction.html
Normal file
72
frontend/html/transaction.html
Normal file
@ -0,0 +1,72 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="description" content="Financial Accounting Tool Project" />
|
||||
<meta
|
||||
name="author"
|
||||
content="Maximilian Ruhm, Tarek Dafay, Christoph van Deest"
|
||||
/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Financial Accounting Tool</title>
|
||||
<link rel="icon" href="assets/favicon.ico" />
|
||||
<link rel="stylesheet" href="../style/index.css" />
|
||||
<link rel="stylesheet" href="../style/transaction.css" />
|
||||
<script type="module" src="../js/transaction.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this Website.</noscript>
|
||||
<div id="root">
|
||||
<div class="header">
|
||||
<span class="title" onclick="window.location.href='../index.html'"
|
||||
>Transaktions-Editor</span
|
||||
><span class="nav" onclick="window.location.href='overview.html'"
|
||||
>Gesamtübersicht</span
|
||||
>
|
||||
</div>
|
||||
<div id="creation-container" class="center">
|
||||
<div class="center form-data">
|
||||
<div class="form-data-label">Buchungsdatum:</div>
|
||||
<input class="form-data-element" type="date" id="postingDate" />
|
||||
</div>
|
||||
<div class="center form-data">
|
||||
<div class="form-data-label">Wertstellungsdatum (Valuta):</div>
|
||||
<input class="form-data-element" type="date" id="valueDate" />
|
||||
</div>
|
||||
<div class="center form-data">
|
||||
<div class="form-data-label">Buchungstitel:</div>
|
||||
<input class="form-data-element" type="text" id="title" />
|
||||
</div>
|
||||
<table class="transaction-table">
|
||||
<thead>
|
||||
<tr class="transaction-tr">
|
||||
<th class="transaction-th" width="30%">Konto</th>
|
||||
<th class="transaction-th" width="25%">Betrag</th>
|
||||
<th class="transaction-th" width="30%">Buchungstext</th>
|
||||
<th class="transaction-th" id="action-row" width="15%" style="text-align: center;"><input type="button" class="action-button" value="+" id="add-transaction" /></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
<div class="save-buttons center">
|
||||
<input
|
||||
class="save-button"
|
||||
type="button"
|
||||
value="abbrechen"
|
||||
id="cancel"
|
||||
/>
|
||||
<input
|
||||
class="save-button"
|
||||
type="button"
|
||||
value="speichern"
|
||||
id="submit"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
Entwicklung des Frontends - Maximilian Ruhm, Tarek Dafay, Christoph van
|
||||
Deest
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
39
frontend/index.html
Normal file
39
frontend/index.html
Normal file
@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="description" content="Financial Accounting Tool Project" />
|
||||
<meta
|
||||
name="author"
|
||||
content="Maximilian Ruhm, Tarek Dafay, Christoph van Deest"
|
||||
/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Financial Accounting Tool</title>
|
||||
<link rel="icon" href="assets/favicon.ico" />
|
||||
<link rel="stylesheet" href="style/index.css" />
|
||||
<link rel="stylesheet" href="style/accordion.css" />
|
||||
<script type="module" src="index.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this Website.</noscript>
|
||||
<div id="root">
|
||||
<div class="header">
|
||||
<span class="title" onclick="window.location.href='./index.html'"
|
||||
>Buchhaltung</span
|
||||
><span class="nav" onclick="window.location.href='html/overview.html'"
|
||||
>Gesamtübersicht</span
|
||||
>
|
||||
</div>
|
||||
<div class="content-wrap">
|
||||
<div class="link-container">
|
||||
<a href="./html/account.html">Konto hinzufügen</a>
|
||||
</div>
|
||||
<div id="accordion-container"></div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
Entwicklung des Frontends - Maximilian Ruhm, Tarek Dafay, Christoph van
|
||||
Deest
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
44
frontend/index.js
Normal file
44
frontend/index.js
Normal file
@ -0,0 +1,44 @@
|
||||
import { fetchData } from "./js/api.js";
|
||||
import { createAccordion } from "./js/accordion.js";
|
||||
|
||||
|
||||
function start() {
|
||||
fetchData("accounts").then((data) => {
|
||||
const container = document.getElementById("accordion-container");
|
||||
container.innerHTML = "";
|
||||
|
||||
data = data.sort((a, b) => (a.number > b.number) ? 1 : ((b.number > a.number) ? -1 : 0));
|
||||
|
||||
data.forEach(element => {
|
||||
const linkName = element.name;
|
||||
|
||||
const childrenContainer = createChildrens(element.subaccounts, linkName);
|
||||
createAccordion(container, element, childrenContainer, linkName);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Funktion mit rekusiven Aufruf um verschachtelte Accordions zu erzeugen
|
||||
* @param {array} subaccounts
|
||||
* @param {string} linkName
|
||||
* @returns
|
||||
*/
|
||||
function createChildrens(subaccounts, linkName) {
|
||||
const childrenContainer = document.createElement("div");
|
||||
childrenContainer.setAttribute("class", "children-container");
|
||||
|
||||
if (subaccounts.length > 0) {
|
||||
subaccounts.forEach(account => {
|
||||
const linkHref = `${linkName}:${account.name}`;
|
||||
const container = createChildrens(account.subaccounts, linkHref);
|
||||
createAccordion(childrenContainer, account, container, linkHref);
|
||||
})
|
||||
|
||||
|
||||
return childrenContainer;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
start();
|
||||
109
frontend/js/accordion.js
Normal file
109
frontend/js/accordion.js
Normal file
@ -0,0 +1,109 @@
|
||||
const euroFormat = new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' });
|
||||
|
||||
/**
|
||||
* Erstellt ein Accordion an einem vorgegebenen Punkt, mit einem Inhalt der übergeben wird
|
||||
* @param {DOMElement} rootElement
|
||||
* @param {object} element
|
||||
* @param {DOMElement} body
|
||||
* @param {string} linkName
|
||||
*/
|
||||
export function createAccordion(rootElement, element, body, linkName) {
|
||||
// accordion
|
||||
const accordion = document.createElement("div");
|
||||
accordion.setAttribute("class", "accordion clickable");
|
||||
|
||||
if (body !== null)
|
||||
accordion.setAttribute("class", "accordion clickable");
|
||||
else
|
||||
accordion.setAttribute("class", "accordion");
|
||||
|
||||
// accordion Titel
|
||||
const accordionTitle = document.createElement("div");
|
||||
accordionTitle.setAttribute("class", "accordion-title");
|
||||
|
||||
// accordion Titel Nummer
|
||||
const numberText = document.createElement("div");
|
||||
numberText.innerHTML = element.number;
|
||||
numberText.setAttribute("class", "title-number");
|
||||
accordionTitle.appendChild(numberText);
|
||||
|
||||
// accordion Titel Name
|
||||
const titleText = document.createElement("div");
|
||||
titleText.setAttribute("class", "title-name");
|
||||
|
||||
const titleLink = document.createElement("a");
|
||||
titleLink.innerHTML = element.name;
|
||||
titleLink.setAttribute("class", "title-link");
|
||||
titleLink.href = `html/detail.html?account=${linkName}`;
|
||||
//titleLink.target = "_blank";
|
||||
|
||||
titleText.appendChild(titleLink);
|
||||
accordionTitle.appendChild(titleText);
|
||||
|
||||
// accordion Titel Beschreibung
|
||||
const descriptionText = document.createElement("div");
|
||||
descriptionText.innerHTML = element.description ? element.description : "";
|
||||
descriptionText.setAttribute("class", "title-description");
|
||||
accordionTitle.appendChild(descriptionText);
|
||||
|
||||
// accordion Titel Wert
|
||||
const balanceText = document.createElement("div");
|
||||
balanceText.innerHTML = euroFormat.format(element.balance);
|
||||
|
||||
if (element.balance < 0)
|
||||
balanceText.setAttribute("class", "title-balance negative-number");
|
||||
else if (element.balance > 0)
|
||||
balanceText.setAttribute("class", "title-balance positive-number");
|
||||
else
|
||||
balanceText.setAttribute("class", "title-balance neutral-number");
|
||||
|
||||
accordionTitle.appendChild(balanceText);
|
||||
|
||||
// accordion Anzeige, ob Unterkonten vorhanden
|
||||
const indicatorText = document.createElement("div");
|
||||
indicatorText.innerHTML = element.subaccounts.length > 0 ? "↓" : "";
|
||||
indicatorText.setAttribute("class", "title-indicator");
|
||||
accordionTitle.appendChild(indicatorText);
|
||||
|
||||
// accordion onclick handler
|
||||
accordionTitle.onclick = () => {
|
||||
if (accordion.querySelector(".accordion-content")) {
|
||||
closeAccordion(accordion);
|
||||
} else {
|
||||
const accordions = document.querySelectorAll(".accordion");
|
||||
//accordions.forEach((accordion) => closeAccordion(accordion));
|
||||
if (body !== null) openAccordion(accordion, body);
|
||||
}
|
||||
}
|
||||
|
||||
accordion.appendChild(accordionTitle);
|
||||
rootElement.appendChild(accordion);
|
||||
}
|
||||
|
||||
/**
|
||||
* Öffnet das Accordion
|
||||
* @param {DOMElement} accordion
|
||||
* @param {DOMElement} body
|
||||
*/
|
||||
function openAccordion(accordion, body) {
|
||||
// accordion Inhalt
|
||||
const accordionContent = document.createElement("div");
|
||||
accordionContent.setAttribute("class", "accordion-content");
|
||||
accordionContent.appendChild(body);
|
||||
|
||||
accordion.appendChild(accordionContent);
|
||||
accordion.setAttribute("class", "accordion clickable open");
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Schließt das Accordion
|
||||
* @param {DOMElement} accordion
|
||||
* @param {DOMElement} body
|
||||
*/
|
||||
function closeAccordion(accordion) {
|
||||
accordion.setAttribute("class", "accordion clickable");
|
||||
const content = accordion.querySelector(".accordion-content");
|
||||
if (content)
|
||||
content.remove();
|
||||
};
|
||||
110
frontend/js/account.js
Normal file
110
frontend/js/account.js
Normal file
@ -0,0 +1,110 @@
|
||||
import { fetchData, postData, putData, deleteData } from "./api.js";
|
||||
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
let accounts = [];
|
||||
let editAccount = null;
|
||||
|
||||
function start() {
|
||||
fetchData("accounts").then((data) => {
|
||||
extractAccounts(data, "");
|
||||
const selectElement = document.getElementById("account-select");
|
||||
|
||||
accounts.forEach(element => {
|
||||
const option = document.createElement("option");
|
||||
option.text = `${element.id} - ${element.name}`;
|
||||
option.value = element.value;
|
||||
selectElement.append(option);
|
||||
});
|
||||
|
||||
// Prüft ob ein Paramter vorhanden ist, wenn ja dann wird das Konto geladen und die Daten zum bearbeiten befüllt
|
||||
if (urlParams.get('account')) {
|
||||
fetchData(`accounts/${urlParams.get('account')}`).then((data) => {
|
||||
document.getElementById("number").value = data.number;
|
||||
document.getElementById("name").value = data.name;
|
||||
document.getElementById("description").value = data.description ? data.description : "";
|
||||
document.getElementById("type").value = data.type;
|
||||
document.getElementById("account-select").value = data.qualifiedName.replace(`:${data.name}`, "");
|
||||
console.log(data.qualifiedName.replace(`:${data.name}`, ""))
|
||||
editAccount = data;
|
||||
document.getElementById("submit").value = "speichern";
|
||||
document.getElementById("delete").style.display = "block";
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Extrahiert die Kontodetails/namen und erstellt dazu ein neues Array
|
||||
* @param {array} data
|
||||
* @param {string} parentName
|
||||
*/
|
||||
function extractAccounts(data, parentName) {
|
||||
data.forEach(element => {
|
||||
let parentAccount = element.name;
|
||||
if (parentName !== "")
|
||||
parentAccount = `${parentName}:${parentAccount}`
|
||||
accounts.push({ id: element.number, name: element.name, value: parentAccount });
|
||||
if (element.subaccounts.length > 0) {
|
||||
extractAccounts(element.subaccounts, parentAccount);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Eventhandler um den Vorgang abzubrechen und zurück zu Startseite zu navigieren
|
||||
*/
|
||||
document.getElementById("cancel").addEventListener("click", () => {
|
||||
window.location.href = "../index.html";
|
||||
});
|
||||
|
||||
/**
|
||||
* Eventhandler um ein Konto zu löschen
|
||||
*/
|
||||
document.getElementById("delete").addEventListener("click", () => {
|
||||
if (editAccount && editAccount.entries.length > 0) {
|
||||
alert("Löschen nur möglich, wenn keine Buchungen stattgefunden haben und keine Unterkonten existieren");
|
||||
return;
|
||||
}
|
||||
deleteData(`accounts/${urlParams.get('account')}`).then((response) => {
|
||||
if (response)
|
||||
window.location.href = "../index.html";
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Eventhandler um ein neues Konto anzulegen bzw. das Konto zu bearbeiten
|
||||
*/
|
||||
document.getElementById("submit").addEventListener("click", () => {
|
||||
if (editAccount && editAccount.entries.length > 0) {
|
||||
alert("Accountbearbeitung nur möglich, wenn keine Buchungen stattgefunden haben");
|
||||
return;
|
||||
}
|
||||
|
||||
const number = document.getElementById("number").value;
|
||||
const name = document.getElementById("name").value;
|
||||
const description = document.getElementById("description").value;
|
||||
const type = document.getElementById("type").value;
|
||||
const parentAccount = document.getElementById("account-select").value;
|
||||
|
||||
const data = {
|
||||
"number": Number.parseInt(number),
|
||||
"name": name,
|
||||
"description": description,
|
||||
"type": type,
|
||||
"parentAccount": parentAccount
|
||||
}
|
||||
|
||||
if (editAccount) {
|
||||
putData(`accounts/${urlParams.get('account')}`, data).then((response) => {
|
||||
if (response)
|
||||
window.location.href = "../index.html";
|
||||
});
|
||||
} else {
|
||||
postData("accounts", data).then((response) => {
|
||||
if (response)
|
||||
window.location.href = "../index.html";
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
start();
|
||||
127
frontend/js/api.js
Normal file
127
frontend/js/api.js
Normal file
@ -0,0 +1,127 @@
|
||||
// API URL
|
||||
// const apiUrl = 'http://149.222.51.77/da4c59b9-43c4-4144-ae39-742c6ba3ad50/api/v1/';
|
||||
const apiUrl = '/api/v1/';
|
||||
|
||||
//SSE URL
|
||||
export const sseUrl = 'http://149.222.51.77/da4c59b9-43c4-4144-ae39-742c6ba3ad50/live';
|
||||
|
||||
export function fetchData(url) {
|
||||
const uri = apiUrl + url;
|
||||
return get(uri);
|
||||
}
|
||||
|
||||
export function postData(url, data) {
|
||||
const uri = apiUrl + url;
|
||||
return post(uri, data);
|
||||
}
|
||||
|
||||
export function putData(url, data) {
|
||||
const uri = apiUrl + url;
|
||||
return put(uri, data);
|
||||
}
|
||||
|
||||
// Make a GET request
|
||||
async function get(url) {
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => { alert("Verbindungsprobleme"); controller.abort(); }, 10000);
|
||||
const response = await fetch(url, { signal: controller.signal });
|
||||
|
||||
if (response.ok) {
|
||||
clearTimeout(timeoutId);
|
||||
return response.json();
|
||||
} else {
|
||||
clearTimeout(timeoutId);
|
||||
if (response.status === 400)
|
||||
alert("Ungültige Anfrage");
|
||||
else if (response.status === 404)
|
||||
alert("Transaktion existiert nicht");
|
||||
else (response.status === 500)
|
||||
alert("Unerwarteter Fehler");
|
||||
}
|
||||
}
|
||||
|
||||
// Make a POST request
|
||||
async function post(url, data) {
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => { alert("Verbindungsprobleme"); controller.abort(); }, 10000);
|
||||
const response = await fetch(url, {
|
||||
signal: controller.signal,
|
||||
headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
|
||||
method: "POST",
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
clearTimeout(timeoutId);
|
||||
alert("Gespeichert!");
|
||||
return true;
|
||||
} else {
|
||||
clearTimeout(timeoutId);
|
||||
if (response.status === 400)
|
||||
alert("Ungültige Anfrage");
|
||||
else if (response.status === 422)
|
||||
alert("Nicht ausführbare Anfrage");
|
||||
else (response.status === 500)
|
||||
alert("Unerwarteter Fehler");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Make a PUT request
|
||||
async function put(url, data) {
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => { alert("Verbindungsprobleme"); controller.abort(); }, 10000);
|
||||
const response = await fetch(url, {
|
||||
signal: controller.signal,
|
||||
headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
|
||||
method: "PUT",
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
clearTimeout(timeoutId);
|
||||
alert("Gespeichert!");
|
||||
return true;
|
||||
} else {
|
||||
clearTimeout(timeoutId);
|
||||
if (response.status === 400)
|
||||
alert("Ungültige Anfrage");
|
||||
else if (response.status === 404)
|
||||
alert("Konto existiert nicht");
|
||||
else if (response.status === 422)
|
||||
alert("Nicht ausführbare Anfrage");
|
||||
else (response.status === 500)
|
||||
alert("Unerwarteter Fehler");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Make a DELETE request
|
||||
export async function deleteData(url) {
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => { alert("Verbindungsprobleme"); controller.abort(); }, 10000);
|
||||
const response = await fetch(apiUrl + url, {
|
||||
signal: controller.signal,
|
||||
method: "DELETE"
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
clearTimeout(timeoutId);
|
||||
alert("Gelöscht!");
|
||||
return true;
|
||||
} else {
|
||||
clearTimeout(timeoutId);
|
||||
if (response.status === 400)
|
||||
alert("Ungültige Anfrage");
|
||||
else if (response.status === 404)
|
||||
alert("Konto existiert nicht");
|
||||
else if (response.status === 422)
|
||||
alert("Nicht ausführbare Anfrage");
|
||||
else (response.status === 500)
|
||||
alert("Unerwarteter Fehler");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
350
frontend/js/detail.js
Normal file
350
frontend/js/detail.js
Normal file
@ -0,0 +1,350 @@
|
||||
import { fetchData, deleteData, sseUrl } from "./api.js";
|
||||
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const euroFormat = new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' });
|
||||
let backendEntries = [];
|
||||
let entries = [];
|
||||
|
||||
// Verbindung zum SSE-Endpunkt herstellen
|
||||
const eventSource = new EventSource(sseUrl);
|
||||
|
||||
// Auf Update reagieren
|
||||
eventSource.addEventListener('update', () => {
|
||||
console.log("refreshing...")
|
||||
start();
|
||||
});
|
||||
|
||||
function start() {
|
||||
fetchData(`accounts/${urlParams.get('account')}`).then((data) => {
|
||||
const root = document.getElementById("detail-container");
|
||||
root.innerHTML = "";
|
||||
|
||||
const title = document.createElement("div");
|
||||
title.innerHTML = `${data.number} - ${data.name}`
|
||||
title.setAttribute("class", "detail-title");
|
||||
root.append(title);
|
||||
|
||||
createDescriptionTable(root, data);
|
||||
|
||||
if (data.description) {
|
||||
const description = document.createElement("div");
|
||||
description.innerHTML = data.description;
|
||||
root.append(description);
|
||||
}
|
||||
|
||||
backendEntries = [...data.entries];
|
||||
entries = [...backendEntries];
|
||||
|
||||
if (entries.length > 0) {
|
||||
document.getElementById("table-container").style.display = "flex";
|
||||
|
||||
//Standardmäßig soll absteigend nach Buchungsdatum sortiert werden
|
||||
entries = entries.sort((a, b) => (a.postingDate < b.postingDate) ? 1 : ((b.postingDate < a.postingDate) ? -1 : 0));
|
||||
//Standardmäßig sollen nur Buchungen des laufenden Kalenderjahres angezeigt werden
|
||||
entries = entries.filter(element => new Date(element.postingDate) >= new Date(new Date().getFullYear(), 0, 1) && new Date(element.valueDate) >= new Date(new Date().getFullYear(), 0, 1));
|
||||
document.getElementById("beginDate").value = `${new Date().getFullYear()}-01-01`;
|
||||
|
||||
createEntryTable(entries);
|
||||
}
|
||||
|
||||
|
||||
if (entries.length < 10)
|
||||
document.getElementById("pagination-div").style.display = "none";
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Erzeugt die Tabelle und errechnet die anzuzeigenden Einträge
|
||||
* @param {array} data
|
||||
*/
|
||||
function createEntryTable(data) {
|
||||
const rootElement = document.querySelector(".table tbody");
|
||||
rootElement.innerHTML = null;
|
||||
|
||||
const bookingDate = document.getElementById("bookingDate");
|
||||
bookingDate.onclick = () => sortTable("postingDate", bookingDate.innerHTML, bookingDate);
|
||||
const valuta = document.getElementById("valuta");
|
||||
valuta.onclick = () => sortTable("valueDate", valuta.innerHTML, valuta);
|
||||
|
||||
const pageInfo = document.getElementById("page-info");
|
||||
const arrayEntriesStart = (pageInfo.innerHTML - 1) * getBookingsPerPage();
|
||||
const arrayEntriesEnd = (pageInfo.innerHTML) * getBookingsPerPage();
|
||||
data = data.slice(arrayEntriesStart, arrayEntriesEnd);
|
||||
|
||||
data.forEach(element => {
|
||||
rootElement.append(createEntryTableRow(element));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Erzeugt die einzelnen Tabellenzeilen
|
||||
* @param {object} entry
|
||||
* @returns DOMElement
|
||||
*/
|
||||
function createEntryTableRow(entry) {
|
||||
const tr = document.createElement("tr");
|
||||
tr.setAttribute("class", "entry-table-tr");
|
||||
|
||||
const bookingDate = document.createElement("td");
|
||||
bookingDate.setAttribute("class", "entry-table-td");
|
||||
bookingDate.innerHTML = new Date(entry.postingDate).toLocaleDateString("de-DE");
|
||||
tr.append(bookingDate);
|
||||
|
||||
const valuta = document.createElement("td");
|
||||
valuta.setAttribute("class", "entry-table-td");
|
||||
valuta.innerHTML = new Date(entry.valueDate).toLocaleDateString("de-DE");
|
||||
tr.append(valuta);
|
||||
|
||||
const title = document.createElement("td");
|
||||
title.setAttribute("class", "entry-table-td");
|
||||
title.innerHTML = entry.title;
|
||||
tr.append(title);
|
||||
|
||||
const offsetAccount = document.createElement("td");
|
||||
offsetAccount.setAttribute("class", "entry-table-td");
|
||||
offsetAccount.innerHTML = entry.offsetAccounts;
|
||||
tr.append(offsetAccount);
|
||||
|
||||
const amount = document.createElement("td");
|
||||
amount.setAttribute("class", "entry-table-td");
|
||||
amount.innerHTML = euroFormat.format(entry.amount);
|
||||
tr.append(amount);
|
||||
console.log(entry)
|
||||
|
||||
const action = document.createElement("td");
|
||||
action.setAttribute("class", "entry-table-td");
|
||||
action.style.textAlign="center";
|
||||
const editBtn = document.createElement("input");
|
||||
editBtn.setAttribute("class", "action-button-edit");
|
||||
editBtn.type = "button";
|
||||
editBtn.value = "edit";
|
||||
editBtn.onclick = () => window.location.href = `transaction.html?account=${urlParams.get('account')}&id=${entry.id}`;
|
||||
action.append(editBtn);
|
||||
|
||||
const deleteBtn = document.createElement("input");
|
||||
deleteBtn.setAttribute("class", "action-button");
|
||||
deleteBtn.type = "button";
|
||||
deleteBtn.value = "x";
|
||||
deleteBtn.onclick = () => deleteTransaction(entry);
|
||||
action.append(deleteBtn);
|
||||
|
||||
tr.append(action);
|
||||
|
||||
return tr;
|
||||
}
|
||||
|
||||
/**
|
||||
* triggert das löschen der Transaktion
|
||||
* @param {object} transaction
|
||||
*/
|
||||
function deleteTransaction(transaction) {
|
||||
deleteData(`transactions/${transaction.id}`).then(response => {
|
||||
if(response)
|
||||
window.location.reload();
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* navigiert zur Seite, zum anlegen neuer Transaktionen
|
||||
*/
|
||||
document.getElementById("add-transaction").addEventListener("click", () => {
|
||||
window.location.href = `transaction.html?account=${urlParams.get('account')}`
|
||||
});
|
||||
|
||||
/**
|
||||
* Erstellt die Detailtabelle
|
||||
* @param {DOMElement} rootElement
|
||||
* @param {object} data
|
||||
*/
|
||||
function createDescriptionTable(rootElement, data) {
|
||||
const table = document.createElement("table");
|
||||
table.setAttribute("class", "description-table");
|
||||
table.setAttribute("border", "1px");
|
||||
|
||||
if (data.description)
|
||||
table.append(createDescriptionTableRow("Kontoart:", data.type));
|
||||
table.append(createDescriptionTableRow("Bilanz (mit Unterkonten):", euroFormat.format(data.balance)));
|
||||
table.append(createDescriptionTableRow("Bilanz (ohne Unterkonten):", euroFormat.format(data.localBalance)));
|
||||
table.append(createDescriptionTableRow("Aktionen", `<a href="account.html?account=${urlParams.get('account')}" class="edit-link">EDIT</a>`));
|
||||
|
||||
|
||||
rootElement.append(table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Erzeugt die einzelnen Tabellenzeilen
|
||||
* @param {string} title
|
||||
* @param {string} value
|
||||
* @returns
|
||||
*/
|
||||
function createDescriptionTableRow(title, value) {
|
||||
const accounttype = document.createElement("tr");
|
||||
accounttype.setAttribute("class", "description-table-tr");
|
||||
|
||||
const accounttypeHeader = document.createElement("th");
|
||||
accounttypeHeader.setAttribute("class", "description-table-th");
|
||||
accounttypeHeader.innerHTML = title;
|
||||
|
||||
const accounttypeValue = document.createElement("td");
|
||||
accounttypeValue.setAttribute("class", "description-table-td");
|
||||
accounttypeValue.innerHTML = value;
|
||||
|
||||
accounttype.append(accounttypeHeader);
|
||||
accounttype.append(accounttypeValue);
|
||||
|
||||
return accounttype;
|
||||
}
|
||||
|
||||
/**
|
||||
* Eventhandler für das sortieren der Tabellen Datumsspalten
|
||||
* Managed die Pfeile zur Sortierrichtung und sortiert das entries array
|
||||
* @param {string} column
|
||||
* @param {string} columnName
|
||||
* @param {DOMElement} docElement
|
||||
*/
|
||||
function sortTable(column, columnName, docElement) {
|
||||
document.querySelectorAll(".clickable").forEach(element => {
|
||||
if (element.innerHTML.slice(-1) === "↓" || element.innerHTML.slice(-1) === "↑")
|
||||
element.innerHTML = element.innerHTML.slice(0, -1);
|
||||
});
|
||||
|
||||
if (columnName.slice(-1) === "↓") { // ASC
|
||||
docElement.innerHTML = `${docElement.innerHTML.slice(0, columnName.length - 1)}↑`;
|
||||
entries = entries.sort((a, b) => (a[column] > b[column]) ? 1 : ((b[column] > a[column]) ? -1 : 0));
|
||||
} else if (columnName.slice(-1) === "↑") { // default
|
||||
docElement.innerHTML = `${docElement.innerHTML.slice(0, columnName.length - 1)}`;
|
||||
entries = backendEntries;
|
||||
} else { // DESC
|
||||
docElement.innerHTML = `${docElement.innerHTML} ↓`;
|
||||
entries = entries.sort((a, b) => (a[column] < b[column]) ? 1 : ((b[column] < a[column]) ? -1 : 0));
|
||||
}
|
||||
|
||||
createEntryTable(entries);
|
||||
}
|
||||
// Fügt den Eventhandler für die Filter Eingabefelder hinzu
|
||||
document.getElementsByName("dateFilter").forEach(element => element.addEventListener("change", filterAll));
|
||||
document.getElementsByName("textFilter").forEach(element => element.addEventListener("keyup", filterAll));
|
||||
|
||||
// Eventhandler um die Datumsfelder zurückzusetzen
|
||||
document.getElementById("resetDates").addEventListener("click", () => {
|
||||
document.getElementById("beginDate").value = "";
|
||||
document.getElementById("endDate").value = "";
|
||||
filterAll();
|
||||
});
|
||||
|
||||
/**
|
||||
* Filtert die Daten aus dem Backend nach den Filterfeldern
|
||||
*/
|
||||
function filterAll() {
|
||||
let filteredEntries = backendEntries;
|
||||
filteredEntries = filterDates(filteredEntries);
|
||||
filteredEntries = filterText(filteredEntries);
|
||||
filteredEntries = filterAmount(filteredEntries);
|
||||
|
||||
createEntryTable(filteredEntries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filtert die Daten anhand der Datumsfelder
|
||||
* @param {array} array
|
||||
* @returns array
|
||||
*/
|
||||
function filterDates(array) {
|
||||
const begin = document.getElementById("beginDate").value;
|
||||
const end = document.getElementById("endDate").value;
|
||||
|
||||
if (begin !== "" && end !== "") {
|
||||
return array.filter(element => (new Date(element.postingDate) >= new Date(begin) && new Date(element.valueDate) >= new Date(begin)) && (new Date(element.postingDate) <= new Date(end) && new Date(element.valueDate) <= new Date(end)));
|
||||
} else if (begin !== "") {
|
||||
return array.filter(element => new Date(element.postingDate) >= new Date(begin) && new Date(element.valueDate) >= new Date(begin));
|
||||
} else if (end !== "") {
|
||||
return array.filter(element => new Date(element.postingDate) <= new Date(end) && new Date(element.valueDate) <= new Date(end));
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filtert die Daten anhand der des Freitextfeldes
|
||||
* @param {array} array
|
||||
* @returns array
|
||||
*/
|
||||
function filterText(array) {
|
||||
const searchString = document.getElementById("searchText").value;
|
||||
if (searchString !== "") {
|
||||
return array.filter(element => element.title.toLowerCase().includes(searchString.toLowerCase()));
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filtert die Daten anhand der des min. und max. Betrags
|
||||
* @param {array} array
|
||||
* @returns array
|
||||
*/
|
||||
function filterAmount(array) {
|
||||
const begin = document.getElementById("beginAmount").value;
|
||||
const end = document.getElementById("endAmount").value;
|
||||
|
||||
if (begin !== "" && end !== "") {
|
||||
return array.filter(element => (element.amount >= begin) && (element.amount <= end));
|
||||
} else if (begin !== "") {
|
||||
return array.filter(element => element.amount >= begin);
|
||||
} else if (end !== "") {
|
||||
return array.filter(element => element.amount <= end);
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Eventhandler für Pagination "Seite Vorwärts Button"
|
||||
*/
|
||||
document.getElementById("forward-page").addEventListener("click", () => {
|
||||
const pageInfo = document.getElementById("page-info");
|
||||
let pageNumber = ++pageInfo.innerHTML;
|
||||
const maxPage = Math.ceil(entries.length / getBookingsPerPage());
|
||||
|
||||
if (pageNumber > maxPage)
|
||||
pageNumber = 1;
|
||||
|
||||
pageInfo.innerHTML = pageNumber;
|
||||
createEntryTable(entries);
|
||||
});
|
||||
|
||||
/**
|
||||
* Eventhandler für Pagination "Seite Rückwärts Button"
|
||||
*/
|
||||
document.getElementById("backward-page").addEventListener("click", () => {
|
||||
const pageInfo = document.getElementById("page-info");
|
||||
let pageNumber = --pageInfo.innerHTML;
|
||||
const maxPage = Math.ceil(entries.length / getBookingsPerPage());
|
||||
|
||||
if (pageNumber < 1)
|
||||
pageNumber = maxPage;
|
||||
|
||||
pageInfo.innerHTML = pageNumber;
|
||||
createEntryTable(entries);
|
||||
});
|
||||
|
||||
/**
|
||||
* Eventhandler für Selectbox Änderung
|
||||
*/
|
||||
document.getElementById("table-range").addEventListener("change", () => {
|
||||
document.getElementById("page-info").innerHTML = 1; //reset page
|
||||
createEntryTable(entries);
|
||||
});
|
||||
|
||||
/**
|
||||
* Liefert die Anzahl der Einträge aus der Selectbox zurück bzw. die Array länge, bei Auswahl von "Alle"
|
||||
* @returns integer
|
||||
*/
|
||||
function getBookingsPerPage() {
|
||||
if (document.getElementById("table-range").value === "Alle")
|
||||
return entries.length;
|
||||
|
||||
return Number.parseInt(document.getElementById("table-range").value);
|
||||
}
|
||||
|
||||
start();
|
||||
214
frontend/js/overview.js
Normal file
214
frontend/js/overview.js
Normal file
@ -0,0 +1,214 @@
|
||||
import { fetchData, sseUrl } from "./api.js";
|
||||
|
||||
let backendEntries = [];
|
||||
let entries = [];
|
||||
|
||||
// Verbindung zum SSE-Endpunkt herstellen
|
||||
const eventSource = new EventSource(sseUrl);
|
||||
|
||||
// Auf Update reagieren
|
||||
eventSource.addEventListener('update', () => {
|
||||
console.log("refreshing...")
|
||||
start();
|
||||
});
|
||||
|
||||
function start() {
|
||||
fetchData(`transactions`).then((data) => {
|
||||
backendEntries = [...data];
|
||||
entries = [...backendEntries];
|
||||
|
||||
if (entries.length > 0) {
|
||||
document.getElementById("table-container").style.display = "flex";
|
||||
|
||||
createTableBody(entries);
|
||||
}
|
||||
|
||||
if (entries.length < 10)
|
||||
document.getElementById("pagination-div").style.display = "none";
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Generiert den Tabelleninhalt
|
||||
* Anhand der angaben aus Seitenzahl und Zeilenangaben werden entsprechend viele Zeilen erstellt
|
||||
* @param {array} data
|
||||
*/
|
||||
function createTableBody(data) {
|
||||
const rootElement = document.querySelector(".table tbody");
|
||||
rootElement.innerHTML = null;
|
||||
|
||||
const bookingDate = document.getElementById("bookingDate");
|
||||
bookingDate.onclick = () => sortTable("postingDate", bookingDate.innerHTML, bookingDate);
|
||||
const valuta = document.getElementById("valuta");
|
||||
valuta.onclick = () => sortTable("valueDate", valuta.innerHTML, valuta);
|
||||
|
||||
const pageInfo = document.getElementById("page-info");
|
||||
const arrayEntriesStart = (pageInfo.innerHTML-1) * getBookingsPerPage();
|
||||
const arrayEntriesEnd = (pageInfo.innerHTML) * getBookingsPerPage();
|
||||
data = data.slice(arrayEntriesStart, arrayEntriesEnd);
|
||||
|
||||
data.forEach(element => {
|
||||
rootElement.append(createEntryTableRow(element));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Erzeugt die einzelne Tabellenzeile
|
||||
* @param {object} entry
|
||||
* @returns DOMElement
|
||||
*/
|
||||
function createEntryTableRow(entry) {
|
||||
const tr = document.createElement("tr");
|
||||
tr.setAttribute("class", "entry-table-tr");
|
||||
|
||||
const bookingDate = document.createElement("td");
|
||||
bookingDate.setAttribute("class", "entry-table-td");
|
||||
bookingDate.innerHTML = new Date(entry.postingDate).toLocaleDateString("de-DE");
|
||||
tr.append(bookingDate);
|
||||
|
||||
const valuta = document.createElement("td");
|
||||
valuta.setAttribute("class", "entry-table-td");
|
||||
valuta.innerHTML = new Date(entry.valueDate).toLocaleDateString("de-DE");
|
||||
tr.append(valuta);
|
||||
|
||||
const title = document.createElement("td");
|
||||
title.setAttribute("class", "entry-table-td");
|
||||
title.innerHTML = entry.title;
|
||||
tr.append(title);
|
||||
|
||||
return tr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Eventhandler für das sortieren der Tabellen Datumsspalten
|
||||
* Managed die Pfeile zur Sortierrichtung und sortiert das entries array
|
||||
* @param {string} column
|
||||
* @param {string} columnName
|
||||
* @param {DOMElement} docElement
|
||||
*/
|
||||
function sortTable(column, columnName, docElement) {
|
||||
document.querySelectorAll(".clickable").forEach(element => {
|
||||
if(element.innerHTML.slice(-1) === "↓" || element.innerHTML.slice(-1) === "↑")
|
||||
element.innerHTML = element.innerHTML.slice(0, -1);
|
||||
});
|
||||
|
||||
if (columnName.slice(-1) === "↓") { // ASC
|
||||
docElement.innerHTML = `${columnName.slice(0, columnName.length - 1)}↑`;
|
||||
entries = entries.sort((a, b) => (a[column] > b[column]) ? 1 : ((b[column] > a[column]) ? -1 : 0));
|
||||
} else if (columnName.slice(-1) === "↑") { // default
|
||||
docElement.innerHTML = `${columnName.slice(0, columnName.length - 1)}`;
|
||||
entries = backendEntries;
|
||||
} else { // DESC
|
||||
docElement.innerHTML = `${columnName} ↓`;
|
||||
entries = entries.sort((a, b) => (a[column] < b[column]) ? 1 : ((b[column] < a[column]) ? -1 : 0));
|
||||
}
|
||||
|
||||
createTableBody(entries);
|
||||
}
|
||||
|
||||
// Fügt den Eventhandler für die Filter Eingabefelder hinzu
|
||||
document.getElementsByName("dateFilter").forEach(element => element.addEventListener("change", filterAll));
|
||||
document.getElementsByName("textFilter").forEach(element => element.addEventListener("keyup", filterAll));
|
||||
|
||||
// Eventhandler um die Datumsfelder zurückzusetzen
|
||||
document.getElementById("resetDates").addEventListener("click", () => {
|
||||
document.getElementById("beginDate").value = "";
|
||||
document.getElementById("endDate").value = "";
|
||||
filterAll();
|
||||
});
|
||||
|
||||
/**
|
||||
* Filtert die Daten aus dem Backend nach den Filterfeldern
|
||||
*/
|
||||
function filterAll() {
|
||||
let filteredEntries = backendEntries;
|
||||
filteredEntries = filterDates(filteredEntries);
|
||||
filteredEntries = filterText(filteredEntries);
|
||||
|
||||
createTableBody(filteredEntries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filtert die Daten anhand der Datumsfelder
|
||||
* @param {array} array
|
||||
* @returns array
|
||||
*/
|
||||
function filterDates(array) {
|
||||
const begin = document.getElementById("beginDate").value;
|
||||
const end = document.getElementById("endDate").value;
|
||||
|
||||
if (begin !== "" && end !== "") {
|
||||
return array.filter(element => (new Date(element.postingDate) >= new Date(begin) && new Date(element.valueDate) >= new Date(begin)) && (new Date(element.postingDate) <= new Date(end) && new Date(element.valueDate) <= new Date(end)));
|
||||
} else if (begin !== "") {
|
||||
return array.filter(element => new Date(element.postingDate) >= new Date(begin) && new Date(element.valueDate) >= new Date(begin));
|
||||
} else if (end !== "") {
|
||||
return array.filter(element => new Date(element.postingDate) <= new Date(end) && new Date(element.valueDate) <= new Date(end));
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filtert die Daten anhand der des Freitextfeldes
|
||||
* @param {array} array
|
||||
* @returns array
|
||||
*/
|
||||
function filterText(array) {
|
||||
const searchString = document.getElementById("searchText").value;
|
||||
if (searchString !== "") {
|
||||
return array.filter(element => element.title.toLowerCase().includes(searchString.toLowerCase()));
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Eventhandler für Pagination "Seite Vorwärts Button"
|
||||
*/
|
||||
document.getElementById("forward-page").addEventListener("click", () => {
|
||||
const pageInfo = document.getElementById("page-info");
|
||||
let pageNumber = ++pageInfo.innerHTML;
|
||||
const maxPage = Math.ceil(entries.length / getBookingsPerPage());
|
||||
|
||||
if(pageNumber > maxPage)
|
||||
pageNumber = 1;
|
||||
|
||||
pageInfo.innerHTML = pageNumber;
|
||||
createTableBody(entries);
|
||||
});
|
||||
|
||||
/**
|
||||
* Eventhandler für Pagination "Seite Rückwärts Button"
|
||||
*/
|
||||
document.getElementById("backward-page").addEventListener("click", () => {
|
||||
const pageInfo = document.getElementById("page-info");
|
||||
let pageNumber = --pageInfo.innerHTML;
|
||||
const maxPage = Math.ceil(entries.length / getBookingsPerPage());
|
||||
|
||||
if(pageNumber < 1)
|
||||
pageNumber = maxPage;
|
||||
|
||||
pageInfo.innerHTML = pageNumber;
|
||||
createTableBody(entries);
|
||||
});
|
||||
|
||||
/**
|
||||
* Eventhandler für Selectbox Änderung
|
||||
*/
|
||||
document.getElementById("table-range").addEventListener("change", () => {
|
||||
document.getElementById("page-info").innerHTML = 1; //reset page
|
||||
createTableBody(entries);
|
||||
});
|
||||
|
||||
/**
|
||||
* Liefert die Anzahl der Einträge aus der Selectbox zurück bzw. die Array länge, bei Auswahl von "Alle"
|
||||
* @returns integer
|
||||
*/
|
||||
function getBookingsPerPage() {
|
||||
if(document.getElementById("table-range").value === "Alle")
|
||||
return entries.length;
|
||||
|
||||
return Number.parseInt(document.getElementById("table-range").value);
|
||||
}
|
||||
|
||||
start();
|
||||
178
frontend/js/transaction.js
Normal file
178
frontend/js/transaction.js
Normal file
@ -0,0 +1,178 @@
|
||||
import { fetchData, postData, putData } from "./api.js";
|
||||
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
let accounts = [];
|
||||
let account = null;
|
||||
let transaction = null;
|
||||
|
||||
function start() {
|
||||
fetchData("accounts").then((data) => {
|
||||
extractAccounts(data, "");
|
||||
|
||||
if (urlParams.get('account')) {
|
||||
fetchData(`accounts/${urlParams.get('account')}`).then((data) => {
|
||||
account = data;
|
||||
|
||||
if (urlParams.get('id')) {
|
||||
fetchData(`transactions/${urlParams.get('id')}`).then((transactionData) => {
|
||||
transaction = transactionData;
|
||||
console.log(transaction)
|
||||
|
||||
document.getElementById("submit").value = "bearbeiten";
|
||||
document.getElementById("postingDate").value = transaction.postingDate;
|
||||
document.getElementById("valueDate").value = transaction.valueDate;
|
||||
document.getElementById("title").value = transaction.title;
|
||||
|
||||
transaction.entries.forEach(entry => {
|
||||
addTableRow(entry);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* extract account hierarchy and create arraylist of all accounts
|
||||
* @param {array} data
|
||||
* @param {string} parentName
|
||||
*/
|
||||
function extractAccounts(data, parentName) {
|
||||
data.forEach(element => {
|
||||
let parentAccount = element.name;
|
||||
if (parentName !== "")
|
||||
parentAccount = `${parentName}:${parentAccount}`
|
||||
accounts.push({ id: element.number, name: element.name, value: parentAccount });
|
||||
if (element.subaccounts.length > 0) {
|
||||
extractAccounts(element.subaccounts, parentAccount);
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Eventhandler um Tabellenzeilen mit leeren Textfeldern hinzuzufügen
|
||||
*/
|
||||
document.getElementById("add-transaction").addEventListener("click", () => {
|
||||
addTableRow();
|
||||
});
|
||||
|
||||
/**
|
||||
* Fügt eine neue Tabellenzeile mit leeren Textfeldern hinzu
|
||||
* @param {array} data
|
||||
*/
|
||||
function addTableRow(data) {
|
||||
const table = document.querySelector(".transaction-table tbody");
|
||||
const tr = document.createElement("tr");
|
||||
tr.className = "transaction-tr";
|
||||
|
||||
const accountTd = document.createElement("td");
|
||||
accountTd.className = "transaction-td";
|
||||
const accountSelect = createAccountSelect()
|
||||
accountTd.appendChild(accountSelect);
|
||||
|
||||
const amountTd = document.createElement("td");
|
||||
amountTd.className = "transaction-td";
|
||||
const amountInput = document.createElement("input");
|
||||
amountInput.className = "transaction-input";
|
||||
amountInput.type = "number";
|
||||
amountInput.name = "amount";
|
||||
amountTd.appendChild(amountInput);
|
||||
|
||||
const bookingTextTd = document.createElement("td");
|
||||
bookingTextTd.className = "transaction-td";
|
||||
const bookingInput = document.createElement("input");
|
||||
bookingInput.className = "transaction-input";
|
||||
bookingInput.name = "text";
|
||||
bookingTextTd.appendChild(bookingInput);
|
||||
|
||||
const actionTd = document.createElement("td");
|
||||
actionTd.className = "transaction-td";
|
||||
const actionButton = document.createElement("input");
|
||||
actionButton.className = "action-button";
|
||||
actionButton.type = "button";
|
||||
actionButton.value = "x";
|
||||
actionButton.onclick = () => tr.remove();
|
||||
actionTd.appendChild(actionButton);
|
||||
|
||||
if(data) {
|
||||
accountSelect.value = data.account;
|
||||
amountInput.value = data.amount;
|
||||
bookingInput.value = data.label;
|
||||
}
|
||||
|
||||
tr.appendChild(accountTd);
|
||||
tr.appendChild(amountTd);
|
||||
tr.appendChild(bookingTextTd);
|
||||
tr.appendChild(actionTd);
|
||||
table.append(tr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Erstellt die Selectbox für Kontenauswahl
|
||||
* @returns DOMElement
|
||||
*/
|
||||
function createAccountSelect() {
|
||||
const selectElement = document.createElement("select");
|
||||
selectElement.className = "transaction-input";
|
||||
selectElement.name = "account";
|
||||
|
||||
const option = document.createElement("option");
|
||||
option.text = "--- Konto wählen ---";
|
||||
option.disabled = true;
|
||||
option.selected = true;
|
||||
selectElement.append(option);
|
||||
|
||||
accounts.forEach(element => {
|
||||
const option = document.createElement("option");
|
||||
option.text = `${element.id} - ${element.value}`;
|
||||
option.value = element.value;
|
||||
selectElement.append(option);
|
||||
});
|
||||
|
||||
return selectElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Eventhandler für das abbrechen und zurücknavigieren zur Homepage
|
||||
*/
|
||||
document.getElementById("cancel").addEventListener("click", () => {
|
||||
window.location.href = "../index.html";
|
||||
});
|
||||
|
||||
/**
|
||||
* Eventhandeler zum senden der Transaktionsdaten
|
||||
* Liest die Werte aus den Eingabefenstern, erzeugt das Objekt und sendet sie zur API
|
||||
*/
|
||||
document.getElementById("submit").addEventListener("click", () => {
|
||||
const postingDate = document.getElementById("postingDate").value;
|
||||
const valueDate = document.getElementById("valueDate").value;
|
||||
const title = document.getElementById("title").value;
|
||||
|
||||
const data = {
|
||||
"postingDate": postingDate,
|
||||
"valueDate": valueDate,
|
||||
"title": title,
|
||||
"entries": []
|
||||
}
|
||||
|
||||
document.querySelectorAll(".transaction-table tbody tr").forEach(tr => {
|
||||
const account = tr.querySelector("[name=account]").value;
|
||||
const amount = Number.parseFloat(tr.querySelector("[name=amount]").value);
|
||||
const text = tr.querySelector("[name=text]").value;
|
||||
data.entries.push({ account: account, amount: amount, label: text });
|
||||
});
|
||||
|
||||
if(transaction) {
|
||||
putData(`transactions/${urlParams.get('id')}`, data).then((response) => {
|
||||
if (response)
|
||||
window.location.href = `./detail.html?account=${urlParams.get('account')}`;
|
||||
});
|
||||
} else {
|
||||
postData("transactions", data).then((response) => {
|
||||
if (response)
|
||||
window.location.href = `./detail.html?account=${urlParams.get('account')}`;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
start();
|
||||
130
frontend/style/accordion.css
Normal file
130
frontend/style/accordion.css
Normal file
@ -0,0 +1,130 @@
|
||||
#accordion-container {
|
||||
width: 95%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.accordion {
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.open > .accordion-title {
|
||||
background-color: #00305d;
|
||||
color: #fff;
|
||||
border-radius: 4px 4px 0px 0px;
|
||||
/* diable text selection */
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.accordion-title {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
background-color: #00305d;
|
||||
color: #fff;
|
||||
border-radius: 4px 4px 4px 4px;
|
||||
/* diable text selection */
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.title-number {
|
||||
padding: 10px;
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
.title-name {
|
||||
padding: 10px;
|
||||
width: 20%;
|
||||
}
|
||||
|
||||
.title-link {
|
||||
color: hotpink;
|
||||
}
|
||||
|
||||
.title-description {
|
||||
padding: 10px;
|
||||
width: 35%;
|
||||
}
|
||||
|
||||
.title-balance {
|
||||
padding: 10px;
|
||||
width: 20%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.title-indicator {
|
||||
padding: 10px;
|
||||
width: 5%;
|
||||
padding-right: 20px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.positive-number {
|
||||
color: rgb(64, 207, 64);
|
||||
}
|
||||
|
||||
.negative-number {
|
||||
color: rgb(255, 51, 10);
|
||||
}
|
||||
|
||||
.neutral-number {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.accordion-content {
|
||||
background-color: #b6b6b6;
|
||||
color: #000;
|
||||
padding: 5px 10px 10px 10px;
|
||||
border-radius: 0px 0px 4px 4px;
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
@media all and (max-width: 1000px) {
|
||||
.accordion-title {
|
||||
text-align: center;
|
||||
background-color: #00305d;
|
||||
color: #fff;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.title-number {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.title-number,
|
||||
.title-name,
|
||||
.title-link,
|
||||
.title-description,
|
||||
.title-balance {
|
||||
padding: 0px;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.title-indicator {
|
||||
padding: 0px;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
32
frontend/style/account.css
Normal file
32
frontend/style/account.css
Normal file
@ -0,0 +1,32 @@
|
||||
#creation-container {
|
||||
width: 80%;
|
||||
margin-top: 30px;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.save-buttons {
|
||||
width: 75%;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.save-button {
|
||||
padding: 10px;
|
||||
width: 40%;
|
||||
margin-left: 20px;
|
||||
margin-right: 20px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.form-data {
|
||||
width: 100%;
|
||||
flex-direction: row;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.form-data-label {
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
.form-data-element {
|
||||
width: 60%;
|
||||
}
|
||||
186
frontend/style/detail.css
Normal file
186
frontend/style/detail.css
Normal file
@ -0,0 +1,186 @@
|
||||
#detail-container {
|
||||
margin-top: 30px;
|
||||
width: 80%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.detail-title {
|
||||
font-size: larger;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.detail-description {
|
||||
}
|
||||
|
||||
.description-table {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.description-table,
|
||||
.description-table-th,
|
||||
.description-table-td {
|
||||
border: 1px solid black;
|
||||
border-collapse: collapse;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.description-table-th {
|
||||
width: 35%;
|
||||
padding: 5px 20px;
|
||||
}
|
||||
|
||||
.description-table-td {
|
||||
width: 65%;
|
||||
padding: 5px 20px;
|
||||
}
|
||||
|
||||
.description-table-td:has(> .edit-link) {
|
||||
width: 65%;
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
.edit-link {
|
||||
background-color: rgb(226, 226, 226);
|
||||
border: 1px solid rgb(176, 176, 176);
|
||||
border-radius: 2.5px;
|
||||
padding: 5px;
|
||||
height: 20px;
|
||||
text-decoration: none;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.edit-link:hover,
|
||||
.edit-link:active {
|
||||
background-color: rgb(204, 204, 204);
|
||||
border: 1px solid rgb(158, 158, 158);
|
||||
color: #000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#table-container {
|
||||
width: 90%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
margin-bottom: 20px;
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
.entry-table {
|
||||
width: 100%;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.entry-table,
|
||||
.entry-table-th,
|
||||
.entry-table-td {
|
||||
border: 1px solid black;
|
||||
border-collapse: collapse;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.entry-table-th {
|
||||
padding: 5px 20px;
|
||||
}
|
||||
|
||||
.entry-table-td {
|
||||
padding: 5px 20px;
|
||||
}
|
||||
|
||||
.filter {
|
||||
width: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.filter-text {
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.filter-div {
|
||||
margin-top: 7.5px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.search-text {
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.amount-text {
|
||||
width: 45%;
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
text-align: right;
|
||||
display: flex;
|
||||
justify-content: right;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.pagination-part {
|
||||
height: 50px;
|
||||
width: 33.33%;
|
||||
}
|
||||
|
||||
.page-button {
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
border: 1px solid black;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.clickable {
|
||||
cursor: pointer;
|
||||
/* diable text selection */
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
th > .action-button {
|
||||
font-size: larger;
|
||||
background-color: rgb(0, 84, 6);
|
||||
color: #fff;
|
||||
border: 1px solid #fff;
|
||||
border-radius: 5px;
|
||||
height: 35px;
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
td > .action-button {
|
||||
background-color: rgb(151, 0, 0);
|
||||
color: #fff;
|
||||
border: 1px solid #fff;
|
||||
border-radius: 5px;
|
||||
height: 35px;
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
td > .action-button-edit {
|
||||
background-color: rgb(228, 228, 228);
|
||||
color: #000;
|
||||
border: 1px solid #8c8c8c;
|
||||
border-radius: 5px;
|
||||
height: 35px;
|
||||
width: 50px;
|
||||
}
|
||||
130
frontend/style/index.css
Normal file
130
frontend/style/index.css
Normal file
@ -0,0 +1,130 @@
|
||||
body {
|
||||
font-family: Verdana, Geneva, Tahoma, sans-serif;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
#root {
|
||||
min-height: 100vh;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.content-wrap {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.link-container {
|
||||
margin-top: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.link-container > a {
|
||||
background-color: #c2c2c2;
|
||||
padding: 7.5px 10px 7.5px 10px;
|
||||
border: 0.5px solid black;
|
||||
border-radius: 5px;
|
||||
color: #000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.link-container > a:hover,
|
||||
.link-container > a:active {
|
||||
background-color: #969696;
|
||||
color: #000;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.header {
|
||||
width: 100%;
|
||||
padding: 15px 0px 15px 0px;
|
||||
margin-bottom: 10px;
|
||||
border-bottom: 2px solid rgb(254, 93, 0);
|
||||
background-color: #00305d;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: x-large;
|
||||
font-weight: 500;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
margin-left: 25px;
|
||||
}
|
||||
|
||||
.nav {
|
||||
margin-left: 5%;
|
||||
cursor: pointer;
|
||||
/* diable text selection */
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.nav:hover,
|
||||
nav:active {
|
||||
color: rgb(170, 170, 170);
|
||||
}
|
||||
|
||||
.footer {
|
||||
width: 100%;
|
||||
font-size: x-small;
|
||||
text-align: center;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
margin-top: auto;
|
||||
border-top: 2px solid rgb(254, 93, 0);
|
||||
background-color: #00305d;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.center {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
input {
|
||||
padding: 5px 12.5px;
|
||||
border: 1px solid rgb(182, 182, 182);
|
||||
border-radius: 2.5px;
|
||||
color: #000;
|
||||
font-size: 14px;
|
||||
height: 25px;
|
||||
-ms-box-sizing: content-box;
|
||||
-moz-box-sizing: content-box;
|
||||
-webkit-box-sizing: content-box;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
select {
|
||||
padding: 5px 12.5px;
|
||||
border: 1px solid rgb(182, 182, 182);
|
||||
border-radius: 2.5px;
|
||||
color: #000;
|
||||
font-size: 14px;
|
||||
height: 25px;
|
||||
-ms-box-sizing: content-box;
|
||||
-moz-box-sizing: content-box;
|
||||
-webkit-box-sizing: content-box;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
input[type="button"] {
|
||||
cursor: pointer;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
input[type="button"]:hover, input[type="button"]:active {
|
||||
background-color: rgb(164, 164, 164);
|
||||
border-color: rgb(81, 81, 81);
|
||||
}
|
||||
83
frontend/style/transaction.css
Normal file
83
frontend/style/transaction.css
Normal file
@ -0,0 +1,83 @@
|
||||
#creation-container {
|
||||
width: 80%;
|
||||
margin-top: 30px;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.save-buttons {
|
||||
width: 75%;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.save-button {
|
||||
padding: 10px;
|
||||
width: 40%;
|
||||
margin-left: 20px;
|
||||
margin-right: 20px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.form-data {
|
||||
width: 100%;
|
||||
flex-direction: row;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.form-data-label {
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
.form-data-element {
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
.transaction-table {
|
||||
width: 100%;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.transaction-table,
|
||||
.transaction-th,
|
||||
.transaction-td {
|
||||
border: 1px solid rgb(105, 105, 105);
|
||||
border-collapse: collapse;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.transaction-th {
|
||||
padding: 5px 20px;
|
||||
}
|
||||
|
||||
.transaction-td {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.transaction-input {
|
||||
width: 100%;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 35px;
|
||||
-ms-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
th > .action-button {
|
||||
font-size: larger;
|
||||
background-color: rgb(0, 84, 6);
|
||||
color: #fff;
|
||||
border: 1px solid #fff;
|
||||
border-radius: 5px;
|
||||
height: 35px;
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
td > .action-button {
|
||||
background-color: rgb(151, 0, 0);
|
||||
color: #fff;
|
||||
border: 1px solid #fff;
|
||||
border-radius: 5px;
|
||||
height: 35px;
|
||||
width: 50px;
|
||||
}
|
||||
10
index.js
10
index.js
@ -2,6 +2,7 @@ const express = require("express");
|
||||
const OpenApiValidator = require("express-openapi-validator");
|
||||
const app = express();
|
||||
const port = 3000;
|
||||
const cors = require("cors")
|
||||
|
||||
//// Swagger
|
||||
const swaggerUi = require("swagger-ui-express");
|
||||
@ -11,8 +12,13 @@ const fs = require("fs");
|
||||
const file = fs.readFileSync("./open_api.yaml", "utf8");
|
||||
const swaggerDocument = YAML.parse(file);
|
||||
|
||||
// Serve the swagger ui
|
||||
app.use("/docs", swaggerUi.serve, swaggerUi.setup(swaggerDocument));
|
||||
|
||||
// use cors middleware
|
||||
app.use(cors())
|
||||
|
||||
|
||||
// Validator
|
||||
app.use(express.json())
|
||||
app.use(
|
||||
@ -35,6 +41,8 @@ app.use((err, req, res, next) => {
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: true }))
|
||||
|
||||
app.use( express.static( __dirname + '/frontend'))
|
||||
|
||||
|
||||
const accountsRoute = require("./routes/accounts");
|
||||
const transactionsRoute = require("./routes/transactions");
|
||||
@ -48,7 +56,7 @@ app.use(`${basepath}/transactions`, transactionsRoute);
|
||||
|
||||
|
||||
app.get("/", (req, res) => {
|
||||
res.send("Hello World!");
|
||||
res.sendFile( path.join( __dirname, 'client', 'index.html'))
|
||||
});
|
||||
|
||||
app.listen(port, () => {
|
||||
|
||||
658
package-lock.json
generated
658
package-lock.json
generated
@ -13,8 +13,12 @@
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.19.2",
|
||||
"express-openapi-validator": "^5.1.6",
|
||||
"npm-watch": "^0.13.0",
|
||||
"swagger-ui-express": "^5.0.0",
|
||||
"yaml": "^2.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@flydotio/dockerfile": "^0.5.7"
|
||||
}
|
||||
},
|
||||
"node_modules/@apidevtools/json-schema-ref-parser": {
|
||||
@ -28,6 +32,25 @@
|
||||
"js-yaml": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@flydotio/dockerfile": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@flydotio/dockerfile/-/dockerfile-0.5.7.tgz",
|
||||
"integrity": "sha512-BVkXM2K/jLLYBFos1gT/bYv0YPJue8L4j0dkIsskJI4JRn6rPA9bZjT4sJzkzhdubZuwRGG9cgxj4Cfgt4lHlw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"chalk": "^5.3.0",
|
||||
"diff": "^5.1.0",
|
||||
"ejs": "^3.1.9",
|
||||
"shell-quote": "^1.8.1",
|
||||
"yargs": "^17.7.2"
|
||||
},
|
||||
"bin": {
|
||||
"dockerfile": "index.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jsdevtools/ono": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz",
|
||||
@ -188,6 +211,42 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/anymatch": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
||||
"dependencies": {
|
||||
"normalize-path": "^3.0.0",
|
||||
"picomatch": "^2.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/append-field": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
|
||||
@ -203,6 +262,17 @@
|
||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
|
||||
},
|
||||
"node_modules/async": {
|
||||
"version": "3.2.5",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz",
|
||||
"integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
||||
},
|
||||
"node_modules/base64-js": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
@ -232,6 +302,17 @@
|
||||
"prebuild-install": "^7.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/binary-extensions": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
||||
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/bindings": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
|
||||
@ -273,6 +354,26 @@
|
||||
"npm": "1.2.8000 || >= 1.4.16"
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/braces": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"dependencies": {
|
||||
"fill-range": "^7.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
|
||||
@ -343,11 +444,83 @@
|
||||
"resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz",
|
||||
"integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ=="
|
||||
},
|
||||
"node_modules/chalk": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
|
||||
"integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^12.17.0 || ^14.13 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/chokidar": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
||||
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
|
||||
"dependencies": {
|
||||
"anymatch": "~3.1.2",
|
||||
"braces": "~3.0.2",
|
||||
"glob-parent": "~5.1.2",
|
||||
"is-binary-path": "~2.1.0",
|
||||
"is-glob": "~4.0.1",
|
||||
"normalize-path": "~3.0.0",
|
||||
"readdirp": "~3.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.10.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/chownr": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
|
||||
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
|
||||
},
|
||||
"node_modules/cliui": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
||||
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.1",
|
||||
"wrap-ansi": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
|
||||
},
|
||||
"node_modules/concat-stream": {
|
||||
"version": "1.6.2",
|
||||
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
|
||||
@ -509,11 +682,41 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/diff": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz",
|
||||
"integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/ee-first": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
|
||||
},
|
||||
"node_modules/ejs": {
|
||||
"version": "3.1.10",
|
||||
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
|
||||
"integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"jake": "^10.8.5"
|
||||
},
|
||||
"bin": {
|
||||
"ejs": "bin/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/encodeurl": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||
@ -549,6 +752,15 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/escalade": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
|
||||
"integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/escape-html": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||
@ -654,6 +866,47 @@
|
||||
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
||||
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="
|
||||
},
|
||||
"node_modules/filelist": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
|
||||
"integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"minimatch": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/filelist/node_modules/brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/filelist/node_modules/minimatch": {
|
||||
"version": "5.1.6",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
|
||||
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/finalhandler": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
|
||||
@ -692,6 +945,19 @@
|
||||
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
|
||||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
@ -700,6 +966,15 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-caller-file": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "6.* || 8.* || >= 10.*"
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
|
||||
@ -723,6 +998,17 @@
|
||||
"resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz",
|
||||
"integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="
|
||||
},
|
||||
"node_modules/glob-parent": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||
"dependencies": {
|
||||
"is-glob": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/gopd": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
|
||||
@ -734,6 +1020,15 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/has-property-descriptors": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
|
||||
@ -823,6 +1118,11 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/ignore-by-default": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
|
||||
"integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA=="
|
||||
},
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
@ -841,11 +1141,92 @@
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/is-binary-path": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
||||
"dependencies": {
|
||||
"binary-extensions": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/is-extglob": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/is-glob": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
||||
"dependencies": {
|
||||
"is-extglob": "^2.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-number": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"engines": {
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
|
||||
},
|
||||
"node_modules/jake": {
|
||||
"version": "10.9.1",
|
||||
"resolved": "https://registry.npmjs.org/jake/-/jake-10.9.1.tgz",
|
||||
"integrity": "sha512-61btcOHNnLnsOdtLgA5efqQWjnSi/vow5HbI7HMdKKWqvrKR1bLK3BPlJn9gcSaP2ewuamUSMB5XEy76KUIS2w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"async": "^3.2.3",
|
||||
"chalk": "^4.0.2",
|
||||
"filelist": "^1.0.4",
|
||||
"minimatch": "^3.1.2"
|
||||
},
|
||||
"bin": {
|
||||
"jake": "bin/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/jake/node_modules/chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
|
||||
@ -945,6 +1326,17 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/minimist": {
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
|
||||
@ -1015,6 +1407,93 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.2.tgz",
|
||||
"integrity": "sha512-/Ib/kloefDy+N0iRTxIUzyGcdW9lzlnca2Jsa5w73bs3npXjg+WInmiX6VY13mIb6SykkthYX/U5t0ukryGqBw==",
|
||||
"dependencies": {
|
||||
"chokidar": "^3.5.2",
|
||||
"debug": "^4",
|
||||
"ignore-by-default": "^1.0.1",
|
||||
"minimatch": "^3.1.2",
|
||||
"pstree.remy": "^1.1.8",
|
||||
"semver": "^7.5.3",
|
||||
"simple-update-notifier": "^2.0.0",
|
||||
"supports-color": "^5.5.0",
|
||||
"touch": "^3.1.0",
|
||||
"undefsafe": "^2.0.5"
|
||||
},
|
||||
"bin": {
|
||||
"nodemon": "bin/nodemon.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/nodemon"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon/node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"dependencies": {
|
||||
"ms": "2.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon/node_modules/has-flag": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemon/node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/nodemon/node_modules/supports-color": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||
"dependencies": {
|
||||
"has-flag": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/npm-watch": {
|
||||
"version": "0.13.0",
|
||||
"resolved": "https://registry.npmjs.org/npm-watch/-/npm-watch-0.13.0.tgz",
|
||||
"integrity": "sha512-MYcgocqCzYA44feZhFoYj69FfSaO0EeRE1gcRcmPaXIpNhUMAhNJ1pwic2C4Hn0OPOQmZKSl90CPgmwvOsVhTg==",
|
||||
"dependencies": {
|
||||
"nodemon": "^3.0.1",
|
||||
"through2": "^4.0.2"
|
||||
},
|
||||
"bin": {
|
||||
"npm-watch": "cli.js"
|
||||
}
|
||||
},
|
||||
"node_modules/object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
@ -1071,6 +1550,17 @@
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
||||
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/prebuild-install": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz",
|
||||
@ -1113,6 +1603,11 @@
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/pstree.remy": {
|
||||
"version": "1.1.8",
|
||||
"resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
|
||||
"integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w=="
|
||||
},
|
||||
"node_modules/pump": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
|
||||
@ -1193,6 +1688,26 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/readdirp": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
||||
"dependencies": {
|
||||
"picomatch": "^2.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/require-directory": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/require-from-string": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
||||
@ -1302,6 +1817,15 @@
|
||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
||||
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
|
||||
},
|
||||
"node_modules/shell-quote": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz",
|
||||
"integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==",
|
||||
"dev": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/side-channel": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
|
||||
@ -1362,6 +1886,17 @@
|
||||
"simple-concat": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/simple-update-notifier": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
|
||||
"integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
|
||||
"dependencies": {
|
||||
"semver": "^7.5.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/statuses": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||
@ -1386,6 +1921,32 @@
|
||||
"safe-buffer": "~5.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-json-comments": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
|
||||
@ -1394,6 +1955,18 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"has-flag": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/swagger-ui-dist": {
|
||||
"version": "5.16.0",
|
||||
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.16.0.tgz",
|
||||
@ -1439,6 +2012,25 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/through2": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz",
|
||||
"integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==",
|
||||
"dependencies": {
|
||||
"readable-stream": "3"
|
||||
}
|
||||
},
|
||||
"node_modules/to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"dependencies": {
|
||||
"is-number": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/toidentifier": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
||||
@ -1447,6 +2039,14 @@
|
||||
"node": ">=0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/touch": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz",
|
||||
"integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==",
|
||||
"bin": {
|
||||
"nodetouch": "bin/nodetouch.js"
|
||||
}
|
||||
},
|
||||
"node_modules/tunnel-agent": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
||||
@ -1475,6 +2075,11 @@
|
||||
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
|
||||
},
|
||||
"node_modules/undefsafe": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
|
||||
"integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA=="
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "5.26.5",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||
@ -1517,6 +2122,23 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
@ -1530,6 +2152,15 @@
|
||||
"node": ">=0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/y18n": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
@ -1545,6 +2176,33 @@
|
||||
"engines": {
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs": {
|
||||
"version": "17.7.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
|
||||
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"cliui": "^8.0.1",
|
||||
"escalade": "^3.1.1",
|
||||
"get-caller-file": "^2.0.5",
|
||||
"require-directory": "^2.1.1",
|
||||
"string-width": "^4.2.3",
|
||||
"y18n": "^5.0.5",
|
||||
"yargs-parser": "^21.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/yargs-parser": {
|
||||
"version": "21.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
|
||||
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
10
package.json
10
package.json
@ -3,9 +3,13 @@
|
||||
"version": "1.0.0",
|
||||
"description": "Our backend",
|
||||
"main": "index.js",
|
||||
"watch": {
|
||||
"run": ["{routes}/*.js", "*.js"]
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"run": "node index.js"
|
||||
"run": "node index.js",
|
||||
"watch": "npm-watch"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
@ -14,7 +18,11 @@
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.19.2",
|
||||
"express-openapi-validator": "^5.1.6",
|
||||
"npm-watch": "^0.13.0",
|
||||
"swagger-ui-express": "^5.0.0",
|
||||
"yaml": "^2.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@flydotio/dockerfile": "^0.5.7"
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,11 +13,14 @@ router.get("/", (req, res) => {
|
||||
res.send(accs);
|
||||
});
|
||||
|
||||
router.post("/", (req, res, next) => {
|
||||
|
||||
|
||||
router.post("/", (req, res) => {
|
||||
const name = req.body.name;
|
||||
const des = req.body.description;
|
||||
const type = req.body.type;
|
||||
const new_acc = db.prepare(`INSERT INTO accounts (name, qualifiedName, description, type, balance, localBalance) VALUES (?, 'test',?, ?, 0, 0)`).run(name, des, type)
|
||||
const qualifiedName = req.body.parentAccount + ":" + name
|
||||
const new_acc = db.prepare(`INSERT INTO accounts (name, qualifiedName, description, type, balance, localBalance) VALUES (?, ? ,?, ?, 0, 0)`).run(name, qualifiedName,des, type)
|
||||
res.status(204).send()
|
||||
|
||||
console.log(new_acc);
|
||||
@ -25,7 +28,7 @@ router.post("/", (req, res, next) => {
|
||||
|
||||
router.get("/:account", (req, res) => {
|
||||
const acc = db
|
||||
.prepare("SELECT * FROM accounts WHERE qualifiedName = ?")
|
||||
.prepare("SELECT * FROM accounts WHERE name = ?")
|
||||
.get(req.params.account);
|
||||
|
||||
if (acc == undefined) {
|
||||
@ -44,10 +47,13 @@ router.get("/:account", (req, res) => {
|
||||
|
||||
|
||||
router.put("/:account", (req, res) => {
|
||||
const oldname = req.params.account;
|
||||
const name = req.body.name;
|
||||
const description = req.body.description;
|
||||
const type = req.body.type
|
||||
const acc = db.prepare(`INSERT INTO accounts (name, description, type) VALUES ('${name}', '${description}', '${type}')`).run()
|
||||
const type = req.body.type;
|
||||
const qualifiedName = req.body.parentAccount + ":" + name;
|
||||
|
||||
const acc = db.prepare(`UPDATE accounts SET description = ?, name = ?, qualifiedName = ?, type = ? WHERE name=?`).run(description, name, qualifiedName, type, oldname);
|
||||
|
||||
if (acc == undefined) {
|
||||
res.status(404).send({
|
||||
@ -56,16 +62,14 @@ router.put("/:account", (req, res) => {
|
||||
additionalPropl: {}
|
||||
})
|
||||
} else {
|
||||
res.status(204).send({
|
||||
OK
|
||||
})
|
||||
res.status(204).send()
|
||||
}
|
||||
});
|
||||
|
||||
router.delete("/:account", (req, res) => {
|
||||
const acc = db.prepare("DELETE FROM accounts WHERE qualifiedName = ? RETURNING *").run(req.params.account)
|
||||
const acc = db.prepare("DELETE FROM accounts WHERE name = ? RETURNING *").run(req.params.account)
|
||||
|
||||
res.status(200).send({OK})
|
||||
res.status(200).send()
|
||||
});
|
||||
|
||||
|
||||
|
||||
@ -2,16 +2,16 @@ const express = require('express');
|
||||
const router = express.Router();
|
||||
|
||||
router.get("/", (req, res) => {
|
||||
res.send([])
|
||||
|
||||
})
|
||||
|
||||
|
||||
router.post("/", (req, res) => {
|
||||
res.send(req.params);
|
||||
})
|
||||
|
||||
router.get("/:id", (req, res) => {
|
||||
res.send(req.params);
|
||||
res.send({})
|
||||
})
|
||||
|
||||
router.put("/:id", (req, res) => {
|
||||
|
||||
@ -1,8 +1,2 @@
|
||||
POST http://localhost:3000/api/v1/accounts/
|
||||
{
|
||||
"number": 23123,
|
||||
"name": "Aktiva",
|
||||
"description": "Girokonto bei der Musterbank eG",
|
||||
"type": "default",
|
||||
"parentAccount": "Aktiva:Barvermögen:Bargeld"
|
||||
}
|
||||
{"number":2000,"name":"Meintest","description":"Hallo","type":"default","parentAccount":"meins"}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user