generated from schreifuchs/wails-template
management of library
This commit is contained in:
parent
e90cdf4e6a
commit
5bedea5312
3
.timer.toml
Normal file
3
.timer.toml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
## dates and their corresponding seconds been here :)
|
||||||
|
[25-03-07]
|
||||||
|
schreifuchs_at_archibald = 407
|
10
README.md
10
README.md
@ -1,12 +1,8 @@
|
|||||||
# wails template
|
# Library Manager
|
||||||
This is my wails-template i am using for the *ICT-Regiomeisterschaften 2025*.
|
|
||||||
It uses vanilla svelte with vite in the Frontend. For styling i use Flowbyte and TailwindCSS. I have setup a pipeline to build windows and linux binaries. For windows i have a working installer.
|
|
||||||
|
|
||||||
Feel free to use this template on your own. To set it up just run ```setup.sh```. If you're not using gitea you will have to rename the ```.gitea``` folder to ```.github```, to use the pipelines.
|
|
||||||
|
|
||||||
Enjoy :)
|
|
||||||
|
|
||||||
|
übung für ICT-Regios 2025
|
||||||
|
|
||||||
|
zeit: 4h 15min
|
||||||
|
|
||||||
## Links
|
## Links
|
||||||
|
|
||||||
|
101
app.go
101
app.go
@ -3,19 +3,22 @@ package main
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"library-manager/model"
|
||||||
|
|
||||||
"github.com/gen2brain/beeep"
|
"github.com/gen2brain/beeep"
|
||||||
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// App struct
|
// App struct
|
||||||
type App struct {
|
type App struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
|
db *gorm.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewApp creates a new App application struct
|
// NewApp creates a new App application struct
|
||||||
func NewApp() *App {
|
func NewApp(db *gorm.DB) *App {
|
||||||
|
|
||||||
return &App{}
|
return &App{db: db}
|
||||||
}
|
}
|
||||||
|
|
||||||
// startup is called when the app starts. The context is saved
|
// startup is called when the app starts. The context is saved
|
||||||
@ -33,3 +36,97 @@ func (a *App) startup(ctx context.Context) {
|
|||||||
func (a *App) Greet(name string) string {
|
func (a *App) Greet(name string) string {
|
||||||
return fmt.Sprintf("Hello %s, It's show time!", name)
|
return fmt.Sprintf("Hello %s, It's show time!", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Authors CRIUD
|
||||||
|
func (a *App) SaveAuthor(au *model.Author) {
|
||||||
|
a.db.Save(au)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) DeleteAuthor(au *model.Author) {
|
||||||
|
a.db.Delete(au)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) GetAuthors() (authors []model.Author) {
|
||||||
|
a.db.Find(&authors)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) SaveBook(au *model.Book) {
|
||||||
|
a.db.Save(au)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) DeleteBook(au *model.Book) {
|
||||||
|
a.db.Delete(au)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) GetBooks() (authors []model.Book) {
|
||||||
|
a.db.Preload("Author").Preload("Lendings").Find(&authors)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (a *App) GetAvailableBooks() (books []model.Book) {
|
||||||
|
var bs []model.Book
|
||||||
|
a.db.Preload("Author").Preload("Lendings").Find(&bs)
|
||||||
|
books = make([]model.Book, 0, len(bs))
|
||||||
|
|
||||||
|
for _, b := range bs {
|
||||||
|
if !a.BookLended(b.ID) {
|
||||||
|
books = append(books, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) BookLended(bId uint) bool {
|
||||||
|
var lendings []model.Lending
|
||||||
|
a.db.Where("book_id = ?", bId).Where("returned = FALSE").Find(&lendings)
|
||||||
|
|
||||||
|
fmt.Println(lendings)
|
||||||
|
|
||||||
|
return len(lendings) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) SaveClient(au *model.Client) {
|
||||||
|
a.db.Save(au)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) DeleteClient(au *model.Client) {
|
||||||
|
a.db.Delete(au)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) GetClients() (authors []model.Client) {
|
||||||
|
a.db.Preload("Lendings").Find(&authors)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) SaveLending(au *model.Lending) string {
|
||||||
|
if a.BookLended(Greater(au.BookID, au.Book.ID)) {
|
||||||
|
return "Book is not available"
|
||||||
|
}
|
||||||
|
a.db.Save(au)
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) ReturnLending(l *model.Lending) {
|
||||||
|
l.Returned = true
|
||||||
|
a.db.Save(l)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) DeleteLending(au *model.Lending) {
|
||||||
|
a.db.Delete(au)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) GetLendings() (lendings []model.Lending) {
|
||||||
|
a.db.Preload("Client").Preload("Book").Where("returned = FALSE").Find(&lendings)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (a *App) GetReturnedLendings() (lendings []model.Lending) {
|
||||||
|
a.db.Preload("Client").Preload("Book").Where("returned = TRUE").Find(&lendings)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (a *App) GetAllLendings() (lendings []model.Lending) {
|
||||||
|
a.db.Preload("Client").Preload("Book").Find(&lendings)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
3
frontend/.timer.toml
Normal file
3
frontend/.timer.toml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
## dates and their corresponding seconds been here :)
|
||||||
|
[25-03-07]
|
||||||
|
schreifuchs_at_archibald = 15333
|
@ -1,25 +1,37 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import "./app.css";
|
import "./app.css";
|
||||||
import { Router, Route, Link, navigate } from "svelte-routing";
|
import { Router, Route, Link, navigate } from "svelte-routing";
|
||||||
import Home from "./routes/Home.svelte";
|
|
||||||
import "./app.css";
|
import "./app.css";
|
||||||
import { Navbar, DarkMode, Heading } from "flowbite-svelte";
|
import { Navbar, DarkMode, Heading } from "flowbite-svelte";
|
||||||
import { HomeOutline } from "flowbite-svelte-icons";
|
import { HomeOutline } from "flowbite-svelte-icons";
|
||||||
|
import Books from "./routes/Books.svelte";
|
||||||
|
import Clients from "./routes/Clients.svelte";
|
||||||
|
import Lendings from "./routes/Lendings.svelte";
|
||||||
let url: string = $state("/");
|
let url: string = $state("/");
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<main class="flex-col h-screen items-center bg-gray-50 dark:bg-gray-900">
|
<main class="flex-col h-screen items-center bg-gray-50 dark:bg-gray-900">
|
||||||
<Router bind:url>
|
<Router bind:url>
|
||||||
<Navbar class="border-b">
|
<Navbar class="border-b">
|
||||||
<button
|
<div class="flex gap-5">
|
||||||
class="grid grid-cols-3 items-center"
|
<button
|
||||||
onclick={() => navigate("/")}
|
class="grid grid-cols-3 items-center"
|
||||||
>
|
onclick={() => navigate("/")}
|
||||||
<HomeOutline />
|
>
|
||||||
<span class="col-span-2">HOME</span>
|
<HomeOutline />
|
||||||
</button>
|
<span class="col-span-2">HOME</span>
|
||||||
|
</button>
|
||||||
|
<Link to="/clients">Clients</Link>
|
||||||
|
<Link to="/books">Books</Link>
|
||||||
|
</div>
|
||||||
<DarkMode />
|
<DarkMode />
|
||||||
</Navbar>
|
</Navbar>
|
||||||
<Route path="/"><Home /></Route>
|
<Route path="/"><Lendings clientId={null} /></Route>
|
||||||
|
<Route path="/books"><Books /></Route>
|
||||||
|
<Route path="/clients"><Clients /></Route>
|
||||||
|
|
||||||
|
<Route path="/clients/:id" let:params>
|
||||||
|
<Lendings clientId={parseInt(params.id)} />
|
||||||
|
</Route>
|
||||||
</Router>
|
</Router>
|
||||||
</main>
|
</main>
|
||||||
|
24
frontend/src/components/AuthorEditor.svelte
Normal file
24
frontend/src/components/AuthorEditor.svelte
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Button, Input, Label } from "flowbite-svelte";
|
||||||
|
import { model } from "../../wailsjs/go/models";
|
||||||
|
|
||||||
|
let {
|
||||||
|
author = new model.Author(),
|
||||||
|
onsubmit,
|
||||||
|
}: { author: model.Author; onsubmit: (a: model.Author) => void } = $props();
|
||||||
|
|
||||||
|
function submit(e: Event) {
|
||||||
|
e.preventDefault();
|
||||||
|
onsubmit(author);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<form onsubmit={submit}>
|
||||||
|
<div class="m-5">
|
||||||
|
<Label>Name</Label>
|
||||||
|
<Input type="text" bind:value={author.Name} />
|
||||||
|
</div>
|
||||||
|
<div class="m-5">
|
||||||
|
<Button type="submit">Save</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
73
frontend/src/components/BookEditor.svelte
Normal file
73
frontend/src/components/BookEditor.svelte
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Button, Input, Label, Modal, Select } from "flowbite-svelte";
|
||||||
|
import { model } from "../../wailsjs/go/models";
|
||||||
|
import { GetAuthors, SaveAuthor } from "../../wailsjs/go/main/App";
|
||||||
|
import AuthorEditor from "./AuthorEditor.svelte";
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
|
||||||
|
const ISBNRegex =
|
||||||
|
"^(?:ISBN(?:-13)?:? )?(?=[0-9]{13}$|(?=(?:[0-9]+[- ]){4})[- 0-9]{17}$)97[89][- ]?[0-9]{1,5}[- ]?[0-9]+[- ]?[0-9]+[- ]?[0-9]$";
|
||||||
|
|
||||||
|
let {
|
||||||
|
book = $bindable(),
|
||||||
|
onsubmit = (_) => {},
|
||||||
|
}: { book: model.Book; onsubmit: (a: model.Book) => void } = $props();
|
||||||
|
|
||||||
|
let authors: model.Author[] = $state([]);
|
||||||
|
|
||||||
|
let modal: boolean = $state(false);
|
||||||
|
let newAuthor: model.Author = $state();
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
GetAuthors().then((as) => (authors = as));
|
||||||
|
}
|
||||||
|
|
||||||
|
function submit(e: Event) {
|
||||||
|
e.preventDefault();
|
||||||
|
onsubmit(book);
|
||||||
|
}
|
||||||
|
onMount(update);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Modal bind:open={modal}>
|
||||||
|
<AuthorEditor
|
||||||
|
author={newAuthor}
|
||||||
|
onsubmit={(a) => {
|
||||||
|
SaveAuthor(a).then(update);
|
||||||
|
modal = false;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<form onsubmit={submit}>
|
||||||
|
<div class="m-5">
|
||||||
|
<Label>Name</Label>
|
||||||
|
<Input type="text" bind:value={book.Title} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="m-5">
|
||||||
|
<Label>ISBN</Label>
|
||||||
|
<Input pattern={ISBNRegex} type="text" bind:value={book.ISBN} />
|
||||||
|
</div>
|
||||||
|
<div class="m-5">
|
||||||
|
<Label>Author</Label>
|
||||||
|
<div class="grid grid-cols-5 gap-5">
|
||||||
|
<Select
|
||||||
|
class="col-span-4"
|
||||||
|
items={authors.map((a) => {
|
||||||
|
return { value: a.ID, name: a.Name };
|
||||||
|
})}
|
||||||
|
bind:value={book.AuthorID}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
onclick={() => {
|
||||||
|
newAuthor = new model.Author();
|
||||||
|
modal = true;
|
||||||
|
}}>New</Button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="m-5">
|
||||||
|
<Button type="submit">Save</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
30
frontend/src/components/ClientEditor.svelte
Normal file
30
frontend/src/components/ClientEditor.svelte
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Button, Input, Label } from "flowbite-svelte";
|
||||||
|
import { model } from "../../wailsjs/go/models";
|
||||||
|
|
||||||
|
let {
|
||||||
|
client = $bindable(),
|
||||||
|
onsubmit = (_) => {},
|
||||||
|
}: { client: model.Client; onsubmit: (a: model.Client) => void } = $props();
|
||||||
|
|
||||||
|
function submit(e: Event) {
|
||||||
|
e.preventDefault();
|
||||||
|
onsubmit(client);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<form onsubmit={submit}>
|
||||||
|
<div class="m-5">
|
||||||
|
<Label>Name</Label>
|
||||||
|
<Input type="text" bind:value={client.Name} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="m-5">
|
||||||
|
<Label>ISBN</Label>
|
||||||
|
<Input type="email" bind:value={client.Email} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="m-5">
|
||||||
|
<Button type="submit">Save</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
73
frontend/src/components/LendingEditor.svelte
Normal file
73
frontend/src/components/LendingEditor.svelte
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Checkbox,
|
||||||
|
Input,
|
||||||
|
Label,
|
||||||
|
Modal,
|
||||||
|
Select,
|
||||||
|
} from "flowbite-svelte";
|
||||||
|
import { model } from "../../wailsjs/go/models";
|
||||||
|
import {
|
||||||
|
GetAuthors,
|
||||||
|
GetAvailableBooks,
|
||||||
|
GetClients,
|
||||||
|
SaveAuthor,
|
||||||
|
} from "../../wailsjs/go/main/App";
|
||||||
|
import AuthorEditor from "./AuthorEditor.svelte";
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
import TimeInput from "./TimeInput.svelte";
|
||||||
|
|
||||||
|
let {
|
||||||
|
lending = $bindable(),
|
||||||
|
onsubmit = (_) => {},
|
||||||
|
}: { lending: model.Lending; onsubmit: (l: model.Lending) => void } =
|
||||||
|
$props();
|
||||||
|
|
||||||
|
let books: model.Book[] = $state([]);
|
||||||
|
let clients: model.Client[] = $state([]);
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
GetAvailableBooks().then((bs) => (books = bs));
|
||||||
|
GetClients().then((cl) => (clients = cl));
|
||||||
|
}
|
||||||
|
|
||||||
|
function submit(e: Event) {
|
||||||
|
e.preventDefault();
|
||||||
|
onsubmit(lending);
|
||||||
|
}
|
||||||
|
onMount(update);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<form onsubmit={submit}>
|
||||||
|
<div class="m-5">
|
||||||
|
<Label>Book</Label>
|
||||||
|
<Select
|
||||||
|
class="col-span-4"
|
||||||
|
items={books.map((b) => {
|
||||||
|
return { value: b.ID, name: b.Title };
|
||||||
|
})}
|
||||||
|
bind:value={lending.BookID}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="m-5">
|
||||||
|
<Label>Client</Label>
|
||||||
|
<Select
|
||||||
|
class="col-span-4"
|
||||||
|
items={clients.map((c) => {
|
||||||
|
return { value: c.ID, name: `${c.Email} ~ ${c.Name}` };
|
||||||
|
})}
|
||||||
|
bind:value={lending.ClientID}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="m-5">
|
||||||
|
<Label>Due Date</Label>
|
||||||
|
<TimeInput bind:value={lending.DueDate} />
|
||||||
|
</div>
|
||||||
|
<div class="m-5">
|
||||||
|
<Checkbox bind:checked={lending.Returned}>Returned</Checkbox>
|
||||||
|
</div>
|
||||||
|
<div class="m-5">
|
||||||
|
<Button type="submit">Save</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Input } from "flowbite-svelte";
|
import { Input } from "flowbite-svelte";
|
||||||
|
|
||||||
let { value }: { value: Date } = $props();
|
let { value = $bindable() }: { value: Date } = $props();
|
||||||
|
|
||||||
const formatDateTimeLocal = (date: Date) => {
|
const formatDateTimeLocal = (date: Date) => {
|
||||||
const pad = (num) => num.toString().padStart(2, "0");
|
const pad = (num) => num.toString().padStart(2, "0");
|
||||||
|
24
frontend/src/routes/AuthorEditor.svelte
Normal file
24
frontend/src/routes/AuthorEditor.svelte
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Button, Input, Label } from "flowbite-svelte";
|
||||||
|
import { model } from "../../wailsjs/go/models";
|
||||||
|
|
||||||
|
let {
|
||||||
|
author = $bindable(),
|
||||||
|
onsubmit = (_) => {},
|
||||||
|
}: { author: model.Author; onsubmit: (a: model.Author) => void } = $props();
|
||||||
|
|
||||||
|
function submit(e: Event) {
|
||||||
|
e.preventDefault();
|
||||||
|
onsubmit(author);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<form onsubmit={submit}>
|
||||||
|
<div class="m-5">
|
||||||
|
<Label>Name</Label>
|
||||||
|
<Input type="text" bind:value={author.Name} />
|
||||||
|
</div>
|
||||||
|
<div class="m-5">
|
||||||
|
<Button type="submit">Save</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
118
frontend/src/routes/Books.svelte
Normal file
118
frontend/src/routes/Books.svelte
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {
|
||||||
|
Badge,
|
||||||
|
Button,
|
||||||
|
Modal,
|
||||||
|
Spinner,
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableBodyCell,
|
||||||
|
TableBodyRow,
|
||||||
|
TableHead,
|
||||||
|
TableHeadCell,
|
||||||
|
} from "flowbite-svelte";
|
||||||
|
import BookEditor from "../components/BookEditor.svelte";
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
import {
|
||||||
|
GetBooks,
|
||||||
|
SaveBook,
|
||||||
|
BookLended,
|
||||||
|
SaveLending,
|
||||||
|
} from "../../wailsjs/go/main/App";
|
||||||
|
import { model } from "../../wailsjs/go/models";
|
||||||
|
import LendingEditor from "../components/LendingEditor.svelte";
|
||||||
|
|
||||||
|
let books: model.Book[] = $state();
|
||||||
|
|
||||||
|
let book: model.Book | null = $state(null);
|
||||||
|
let lending: model.Lending | null = $state(null);
|
||||||
|
let modal: boolean = $state(false);
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
GetBooks().then((bs) => (books = bs));
|
||||||
|
}
|
||||||
|
onMount(update);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if book}
|
||||||
|
<Modal bind:open={modal} title="Book">
|
||||||
|
<BookEditor
|
||||||
|
bind:book
|
||||||
|
onsubmit={(b) => {
|
||||||
|
SaveBook(b).then(update);
|
||||||
|
modal = false;
|
||||||
|
book = null;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
{:else if lending}
|
||||||
|
<Modal bind:open={modal} title="New Lending">
|
||||||
|
<LendingEditor
|
||||||
|
bind:lending
|
||||||
|
onsubmit={(l) => {
|
||||||
|
SaveLending(l).then(update);
|
||||||
|
modal = false;
|
||||||
|
lending = null;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<Table>
|
||||||
|
<TableHead>
|
||||||
|
<TableHeadCell>Title</TableHeadCell>
|
||||||
|
<TableHeadCell>Author</TableHeadCell>
|
||||||
|
<TableHeadCell>ISBN</TableHeadCell>
|
||||||
|
<TableHeadCell>Status</TableHeadCell>
|
||||||
|
|
||||||
|
<TableHeadCell>
|
||||||
|
<Button
|
||||||
|
onclick={() => {
|
||||||
|
book = new model.Book();
|
||||||
|
modal = true;
|
||||||
|
}}>New</Button
|
||||||
|
></TableHeadCell
|
||||||
|
>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{#each books as b}
|
||||||
|
<TableBodyRow>
|
||||||
|
<TableBodyCell>{b.Title}</TableBodyCell>
|
||||||
|
<TableBodyCell>{b.Author.Name}</TableBodyCell>
|
||||||
|
<TableBodyCell>{b.ISBN}</TableBodyCell>
|
||||||
|
<TableBodyCell
|
||||||
|
>{#await BookLended(b.ID)}
|
||||||
|
<Spinner />
|
||||||
|
{:then lended}
|
||||||
|
{#if lended}
|
||||||
|
<Badge color="red">Lended</Badge>
|
||||||
|
{:else}
|
||||||
|
<Badge class="text-sm" color="green">Available</Badge>
|
||||||
|
{/if}
|
||||||
|
{/await}</TableBodyCell
|
||||||
|
>
|
||||||
|
|
||||||
|
<TableBodyCell>
|
||||||
|
{#await BookLended(b.ID) then lended}
|
||||||
|
{#if !lended}
|
||||||
|
<Button
|
||||||
|
onclick={() => {
|
||||||
|
lending = new model.Lending();
|
||||||
|
lending.BookID = b.ID;
|
||||||
|
lending.DueDate = new Date();
|
||||||
|
modal = true;
|
||||||
|
}}>Lend</Button
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
|
{/await}
|
||||||
|
<Button
|
||||||
|
onclick={() => {
|
||||||
|
book = b;
|
||||||
|
modal = true;
|
||||||
|
}}>Edit</Button
|
||||||
|
>
|
||||||
|
</TableBodyCell>
|
||||||
|
</TableBodyRow>
|
||||||
|
{/each}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
107
frontend/src/routes/Clients.svelte
Normal file
107
frontend/src/routes/Clients.svelte
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Modal,
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableBodyCell,
|
||||||
|
TableBodyRow,
|
||||||
|
TableHead,
|
||||||
|
TableHeadCell,
|
||||||
|
} from "flowbite-svelte";
|
||||||
|
import ClientEditor from "../components/ClientEditor.svelte";
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
import {
|
||||||
|
GetClients,
|
||||||
|
SaveClient,
|
||||||
|
SaveLending,
|
||||||
|
} from "../../wailsjs/go/main/App";
|
||||||
|
import { model } from "../../wailsjs/go/models";
|
||||||
|
import LendingEditor from "../components/LendingEditor.svelte";
|
||||||
|
import { navigate } from "svelte-routing";
|
||||||
|
|
||||||
|
let clients: model.Client[] = $state();
|
||||||
|
|
||||||
|
let client: model.Client | null = $state(null);
|
||||||
|
let lending: model.Lending | null = $state(null);
|
||||||
|
let modal: boolean = $state(false);
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
GetClients().then((cs) => (clients = cs));
|
||||||
|
}
|
||||||
|
onMount(update);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if client}
|
||||||
|
<Modal bind:open={modal} title="Client">
|
||||||
|
<ClientEditor
|
||||||
|
{client}
|
||||||
|
onsubmit={(c) => {
|
||||||
|
SaveClient(c).then(update);
|
||||||
|
modal = false;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Modal>{:else if lending}
|
||||||
|
<Modal bind:open={modal} title="New Lending">
|
||||||
|
<LendingEditor
|
||||||
|
bind:lending
|
||||||
|
onsubmit={(l) => {
|
||||||
|
SaveLending(l).then(update);
|
||||||
|
modal = false;
|
||||||
|
lending = null;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
{/if}
|
||||||
|
<Table>
|
||||||
|
<TableHead>
|
||||||
|
<TableHeadCell>Name</TableHeadCell>
|
||||||
|
<TableHeadCell>Email</TableHeadCell>
|
||||||
|
<TableHeadCell>Active Lendings</TableHeadCell>
|
||||||
|
<TableHeadCell>Overdue Lendings</TableHeadCell>
|
||||||
|
<TableHeadCell>
|
||||||
|
<Button
|
||||||
|
onclick={() => {
|
||||||
|
client = new model.Client();
|
||||||
|
modal = true;
|
||||||
|
}}>New</Button
|
||||||
|
></TableHeadCell
|
||||||
|
>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{#each clients as c}
|
||||||
|
<TableBodyRow>
|
||||||
|
<TableBodyCell>{c.Name}</TableBodyCell>
|
||||||
|
<TableBodyCell>{c.Email}</TableBodyCell>
|
||||||
|
<TableBodyCell
|
||||||
|
>{c.Lendings.filter((l) => {
|
||||||
|
return !l.Returned;
|
||||||
|
}).length}
|
||||||
|
</TableBodyCell>
|
||||||
|
|
||||||
|
<TableBodyCell
|
||||||
|
>{c.Lendings.filter((l) => {
|
||||||
|
return new Date(l.DueDate).getTime() > Date.now();
|
||||||
|
}).length}
|
||||||
|
</TableBodyCell>
|
||||||
|
<TableBodyCell>
|
||||||
|
<Button onclick={() => navigate(`/clients/${c.ID}`)}>View</Button>
|
||||||
|
<Button
|
||||||
|
onclick={() => {
|
||||||
|
lending = new model.Lending();
|
||||||
|
lending.DueDate = new Date();
|
||||||
|
lending.ClientID = c.ID;
|
||||||
|
modal = true;
|
||||||
|
}}>Lend a Book</Button
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
onclick={() => {
|
||||||
|
client = c;
|
||||||
|
modal = true;
|
||||||
|
}}>Edit</Button
|
||||||
|
>
|
||||||
|
</TableBodyCell>
|
||||||
|
</TableBodyRow>
|
||||||
|
{/each}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
@ -1,74 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { onMount } from "svelte";
|
|
||||||
import {
|
|
||||||
GetThings,
|
|
||||||
DeleteThing,
|
|
||||||
NewThing,
|
|
||||||
} from "../../wailsjs/go/things/Service";
|
|
||||||
import { model } from "../../wailsjs/go/models";
|
|
||||||
import {
|
|
||||||
Label,
|
|
||||||
Input,
|
|
||||||
Button,
|
|
||||||
Table,
|
|
||||||
TableHead,
|
|
||||||
TableHeadCell,
|
|
||||||
TableBody,
|
|
||||||
TableBodyRow,
|
|
||||||
TableBodyCell,
|
|
||||||
} from "flowbite-svelte";
|
|
||||||
|
|
||||||
let name: string = $state();
|
|
||||||
let thingsList: model.Thing[] = $state([]);
|
|
||||||
|
|
||||||
function update() {
|
|
||||||
GetThings().then((ts) => {
|
|
||||||
thingsList = ts;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function submit(e: Event) {
|
|
||||||
e.preventDefault();
|
|
||||||
NewThing(name).then(update);
|
|
||||||
name = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteEvent(id: number) {
|
|
||||||
DeleteThing(id).then(update);
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(update);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<form class="max-w-96 m-5 grid-cols-1 gap-10" onsubmit={submit}>
|
|
||||||
<div class="m-5">
|
|
||||||
<Label for="first_name" class="mb-2">First name</Label>
|
|
||||||
<Input type="text" placeholder="John" bind:value={name} required />
|
|
||||||
</div>
|
|
||||||
<div class="m-5">
|
|
||||||
<Button type="submit">Submit</Button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<Table>
|
|
||||||
<TableHead>
|
|
||||||
<TableHeadCell>ID</TableHeadCell>
|
|
||||||
<TableHeadCell>Name</TableHeadCell>
|
|
||||||
<TableHeadCell>Delete</TableHeadCell>
|
|
||||||
</TableHead>
|
|
||||||
<TableBody>
|
|
||||||
{#each thingsList as t}
|
|
||||||
<TableBodyRow>
|
|
||||||
<TableBodyCell>
|
|
||||||
{t.ID}
|
|
||||||
</TableBodyCell>
|
|
||||||
|
|
||||||
<TableBodyCell>
|
|
||||||
{t.Name}
|
|
||||||
</TableBodyCell>
|
|
||||||
<TableBodyCell>
|
|
||||||
<Button on:click={(_) => deleteEvent(t.ID)}>Delete</Button>
|
|
||||||
</TableBodyCell>
|
|
||||||
</TableBodyRow>
|
|
||||||
{/each}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
188
frontend/src/routes/Lendings.svelte
Normal file
188
frontend/src/routes/Lendings.svelte
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import {
|
||||||
|
GetReturnedLendings,
|
||||||
|
GetLendings,
|
||||||
|
ReturnLending,
|
||||||
|
SaveLending,
|
||||||
|
DeleteLending,
|
||||||
|
} from "../../wailsjs/go/main/App";
|
||||||
|
import { model } from "../../wailsjs/go/models";
|
||||||
|
import { BrowserOpenURL } from "../../wailsjs/runtime/runtime";
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Heading,
|
||||||
|
Modal,
|
||||||
|
P,
|
||||||
|
Timeline,
|
||||||
|
TimelineItem,
|
||||||
|
} from "flowbite-svelte";
|
||||||
|
import LendingEditor from "../components/LendingEditor.svelte";
|
||||||
|
|
||||||
|
let { clientId = null }: { clientId: number | null } = $props();
|
||||||
|
|
||||||
|
let lendings: model.Lending[] = $state([]);
|
||||||
|
let returnedLendings: model.Lending[] = $state([]);
|
||||||
|
|
||||||
|
let lending: model.Lending = $state();
|
||||||
|
let modal: boolean = $state(false);
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
GetLendings().then((ls) => {
|
||||||
|
lendings = ls.filter((l) => {
|
||||||
|
if (clientId && l.ClientID !== clientId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
GetReturnedLendings().then((ls) => (returnedLendings = ls.filter((l) => {
|
||||||
|
if (clientId && l.ClientID !== clientId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
onMount(update);
|
||||||
|
//
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Modal bind:open={modal} title="Lending">
|
||||||
|
<LendingEditor
|
||||||
|
bind:lending
|
||||||
|
onsubmit={(l) => {
|
||||||
|
SaveLending(l).then(update);
|
||||||
|
modal = false;
|
||||||
|
lending = null;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<div class="m-5 grid grid-cols-5">
|
||||||
|
<Heading tag="h1" class="col-span-4">Lendings</Heading>
|
||||||
|
<Button
|
||||||
|
onclick={() => {
|
||||||
|
lending = new model.Lending();
|
||||||
|
lending.DueDate = new Date();
|
||||||
|
if (clientId) {
|
||||||
|
lending.ClientID = clientId
|
||||||
|
}
|
||||||
|
modal = true;
|
||||||
|
}}>New</Button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section class="m-5 mb-20">
|
||||||
|
<Heading tag="h2" class="mb-5">Overdue</Heading>
|
||||||
|
{#if lendings.filter((l) => {
|
||||||
|
return new Date(l.DueDate).getTime() < Date.now();
|
||||||
|
}).length == 0}
|
||||||
|
<P>No Entries</P>
|
||||||
|
{/if}
|
||||||
|
<Timeline>
|
||||||
|
{#each lendings.filter((l) => {
|
||||||
|
return new Date(l.DueDate).getTime() < Date.now();
|
||||||
|
}) as l}
|
||||||
|
<TimelineItem
|
||||||
|
title={`"${l.Book.Title}" is lended to ${l.Client.Name}`}
|
||||||
|
date={new Date(l.DueDate).toLocaleString()}
|
||||||
|
>
|
||||||
|
<P>
|
||||||
|
The book "{l.Book.Title}" (ISBN: {l.Book.ISBN}) is lended to {l.Client
|
||||||
|
.Name} (<button
|
||||||
|
class="underline"
|
||||||
|
onclick={() => BrowserOpenURL(`mailto:${l.Client.Email}`)}
|
||||||
|
>{l.Client.Email}</button
|
||||||
|
>)
|
||||||
|
</P>
|
||||||
|
<div class="flex gap-5 my-5">
|
||||||
|
<Button
|
||||||
|
onclick={() => {
|
||||||
|
lending = l;
|
||||||
|
modal = true;
|
||||||
|
}}>Edit</Button
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
color="red"
|
||||||
|
onclick={() => {
|
||||||
|
ReturnLending(l).then(update);
|
||||||
|
}}>Set to Returned</Button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</TimelineItem>
|
||||||
|
{/each}
|
||||||
|
</Timeline>
|
||||||
|
</section>
|
||||||
|
<section class="m-5 mb-20">
|
||||||
|
<Heading tag="h2" class="mb-5">Active</Heading>
|
||||||
|
{#if lendings.filter((l) => {
|
||||||
|
return new Date(l.DueDate).getTime() > Date.now();
|
||||||
|
}).length == 0}
|
||||||
|
<P>No Entries</P>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<Timeline>
|
||||||
|
{#each lendings.filter((l) => {
|
||||||
|
return new Date(l.DueDate).getTime() > Date.now();
|
||||||
|
}) as l}
|
||||||
|
<TimelineItem
|
||||||
|
title={`"${l.Book.Title}" is lended to ${l.Client.Name}`}
|
||||||
|
date={new Date(l.DueDate).toLocaleString()}
|
||||||
|
>
|
||||||
|
<P>
|
||||||
|
The book "{l.Book.Title}" (ISBN: {l.Book.ISBN}) is lended to {l.Client
|
||||||
|
.Name} (<button
|
||||||
|
class="underline"
|
||||||
|
onclick={() => BrowserOpenURL(`mailto:${l.Client.Email}`)}
|
||||||
|
>{l.Client.Email}</button
|
||||||
|
>)
|
||||||
|
</P>
|
||||||
|
<div class="flex gap-5 my-5">
|
||||||
|
<Button
|
||||||
|
onclick={() => {
|
||||||
|
lending = l;
|
||||||
|
modal = true;
|
||||||
|
}}>Edit</Button
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
color="red"
|
||||||
|
onclick={() => {
|
||||||
|
ReturnLending(l).then(update);
|
||||||
|
}}>Set to Returned</Button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</TimelineItem>
|
||||||
|
{/each}
|
||||||
|
</Timeline>
|
||||||
|
</section>
|
||||||
|
<section class="m-5 mb-20">
|
||||||
|
<Heading tag="h2" class="mb-5">Returned</Heading>
|
||||||
|
{#if returnedLendings.length == 0}
|
||||||
|
<P>No Entries</P>
|
||||||
|
{/if}
|
||||||
|
<Timeline>
|
||||||
|
{#each returnedLendings as l}
|
||||||
|
<TimelineItem
|
||||||
|
title={`"${l.Book.Title}" was lended to ${l.Client.Name}`}
|
||||||
|
date={new Date(l.DueDate).toLocaleString()}
|
||||||
|
>
|
||||||
|
<P>
|
||||||
|
The book "{l.Book.Title}" (ISBN: {l.Book.ISBN}) was lended to {l
|
||||||
|
.Client.Name} (<button
|
||||||
|
class="underline"
|
||||||
|
onclick={() => BrowserOpenURL(`mailto:${l.Client.Email}`)}
|
||||||
|
>{l.Client.Email}</button
|
||||||
|
>)
|
||||||
|
</P>
|
||||||
|
<div class="flex gap-5 my-5">
|
||||||
|
<Button
|
||||||
|
color="red"
|
||||||
|
onclick={() => {
|
||||||
|
DeleteLending(l).then(update);
|
||||||
|
}}>Delete</Button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</TimelineItem>
|
||||||
|
{/each}
|
||||||
|
</Timeline>
|
||||||
|
</section>
|
35
frontend/wailsjs/go/main/App.d.ts
vendored
35
frontend/wailsjs/go/main/App.d.ts
vendored
@ -1,4 +1,39 @@
|
|||||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
// This file is automatically generated. DO NOT EDIT
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
import {model} from '../models';
|
||||||
|
|
||||||
|
export function BookLended(arg1:number):Promise<boolean>;
|
||||||
|
|
||||||
|
export function DeleteAuthor(arg1:model.Author):Promise<void>;
|
||||||
|
|
||||||
|
export function DeleteBook(arg1:model.Book):Promise<void>;
|
||||||
|
|
||||||
|
export function DeleteClient(arg1:model.Client):Promise<void>;
|
||||||
|
|
||||||
|
export function DeleteLending(arg1:model.Lending):Promise<void>;
|
||||||
|
|
||||||
|
export function GetAllLendings():Promise<Array<model.Lending>>;
|
||||||
|
|
||||||
|
export function GetAuthors():Promise<Array<model.Author>>;
|
||||||
|
|
||||||
|
export function GetAvailableBooks():Promise<Array<model.Book>>;
|
||||||
|
|
||||||
|
export function GetBooks():Promise<Array<model.Book>>;
|
||||||
|
|
||||||
|
export function GetClients():Promise<Array<model.Client>>;
|
||||||
|
|
||||||
|
export function GetLendings():Promise<Array<model.Lending>>;
|
||||||
|
|
||||||
|
export function GetReturnedLendings():Promise<Array<model.Lending>>;
|
||||||
|
|
||||||
export function Greet(arg1:string):Promise<string>;
|
export function Greet(arg1:string):Promise<string>;
|
||||||
|
|
||||||
|
export function ReturnLending(arg1:model.Lending):Promise<void>;
|
||||||
|
|
||||||
|
export function SaveAuthor(arg1:model.Author):Promise<void>;
|
||||||
|
|
||||||
|
export function SaveBook(arg1:model.Book):Promise<void>;
|
||||||
|
|
||||||
|
export function SaveClient(arg1:model.Client):Promise<void>;
|
||||||
|
|
||||||
|
export function SaveLending(arg1:model.Lending):Promise<string>;
|
||||||
|
@ -2,6 +2,74 @@
|
|||||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
// This file is automatically generated. DO NOT EDIT
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
export function BookLended(arg1) {
|
||||||
|
return window['go']['main']['App']['BookLended'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DeleteAuthor(arg1) {
|
||||||
|
return window['go']['main']['App']['DeleteAuthor'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DeleteBook(arg1) {
|
||||||
|
return window['go']['main']['App']['DeleteBook'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DeleteClient(arg1) {
|
||||||
|
return window['go']['main']['App']['DeleteClient'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DeleteLending(arg1) {
|
||||||
|
return window['go']['main']['App']['DeleteLending'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GetAllLendings() {
|
||||||
|
return window['go']['main']['App']['GetAllLendings']();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GetAuthors() {
|
||||||
|
return window['go']['main']['App']['GetAuthors']();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GetAvailableBooks() {
|
||||||
|
return window['go']['main']['App']['GetAvailableBooks']();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GetBooks() {
|
||||||
|
return window['go']['main']['App']['GetBooks']();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GetClients() {
|
||||||
|
return window['go']['main']['App']['GetClients']();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GetLendings() {
|
||||||
|
return window['go']['main']['App']['GetLendings']();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GetReturnedLendings() {
|
||||||
|
return window['go']['main']['App']['GetReturnedLendings']();
|
||||||
|
}
|
||||||
|
|
||||||
export function Greet(arg1) {
|
export function Greet(arg1) {
|
||||||
return window['go']['main']['App']['Greet'](arg1);
|
return window['go']['main']['App']['Greet'](arg1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function ReturnLending(arg1) {
|
||||||
|
return window['go']['main']['App']['ReturnLending'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SaveAuthor(arg1) {
|
||||||
|
return window['go']['main']['App']['SaveAuthor'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SaveBook(arg1) {
|
||||||
|
return window['go']['main']['App']['SaveBook'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SaveClient(arg1) {
|
||||||
|
return window['go']['main']['App']['SaveClient'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SaveLending(arg1) {
|
||||||
|
return window['go']['main']['App']['SaveLending'](arg1);
|
||||||
|
}
|
||||||
|
@ -1,35 +1,30 @@
|
|||||||
export namespace model {
|
export namespace model {
|
||||||
|
|
||||||
export class SubThing {
|
export class Client {
|
||||||
ID: number;
|
ID: number;
|
||||||
ThingID: number;
|
// Go type: time
|
||||||
|
CreatedAt: any;
|
||||||
|
// Go type: time
|
||||||
|
UpdatedAt: any;
|
||||||
|
// Go type: gorm
|
||||||
|
DeletedAt: any;
|
||||||
Name: string;
|
Name: string;
|
||||||
|
Email: string;
|
||||||
|
Lendings: Lending[];
|
||||||
|
|
||||||
static createFrom(source: any = {}) {
|
static createFrom(source: any = {}) {
|
||||||
return new SubThing(source);
|
return new Client(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(source: any = {}) {
|
constructor(source: any = {}) {
|
||||||
if ('string' === typeof source) source = JSON.parse(source);
|
if ('string' === typeof source) source = JSON.parse(source);
|
||||||
this.ID = source["ID"];
|
this.ID = source["ID"];
|
||||||
this.ThingID = source["ThingID"];
|
this.CreatedAt = this.convertValues(source["CreatedAt"], null);
|
||||||
|
this.UpdatedAt = this.convertValues(source["UpdatedAt"], null);
|
||||||
|
this.DeletedAt = this.convertValues(source["DeletedAt"], null);
|
||||||
this.Name = source["Name"];
|
this.Name = source["Name"];
|
||||||
}
|
this.Email = source["Email"];
|
||||||
}
|
this.Lendings = this.convertValues(source["Lendings"], Lending);
|
||||||
export class Thing {
|
|
||||||
ID: number;
|
|
||||||
Name: string;
|
|
||||||
Subthings: SubThing[];
|
|
||||||
|
|
||||||
static createFrom(source: any = {}) {
|
|
||||||
return new Thing(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(source: any = {}) {
|
|
||||||
if ('string' === typeof source) source = JSON.parse(source);
|
|
||||||
this.ID = source["ID"];
|
|
||||||
this.Name = source["Name"];
|
|
||||||
this.Subthings = this.convertValues(source["Subthings"], SubThing);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
convertValues(a: any, classs: any, asMap: boolean = false): any {
|
convertValues(a: any, classs: any, asMap: boolean = false): any {
|
||||||
@ -50,6 +45,152 @@ export namespace model {
|
|||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export class Lending {
|
||||||
|
ID: number;
|
||||||
|
// Go type: time
|
||||||
|
CreatedAt: any;
|
||||||
|
// Go type: time
|
||||||
|
UpdatedAt: any;
|
||||||
|
// Go type: gorm
|
||||||
|
DeletedAt: any;
|
||||||
|
// Go type: time
|
||||||
|
DueDate: any;
|
||||||
|
Returned: boolean;
|
||||||
|
ClientID: number;
|
||||||
|
Client: Client;
|
||||||
|
BookID: number;
|
||||||
|
Book: Book;
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new Lending(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) source = JSON.parse(source);
|
||||||
|
this.ID = source["ID"];
|
||||||
|
this.CreatedAt = this.convertValues(source["CreatedAt"], null);
|
||||||
|
this.UpdatedAt = this.convertValues(source["UpdatedAt"], null);
|
||||||
|
this.DeletedAt = this.convertValues(source["DeletedAt"], null);
|
||||||
|
this.DueDate = this.convertValues(source["DueDate"], null);
|
||||||
|
this.Returned = source["Returned"];
|
||||||
|
this.ClientID = source["ClientID"];
|
||||||
|
this.Client = this.convertValues(source["Client"], Client);
|
||||||
|
this.BookID = source["BookID"];
|
||||||
|
this.Book = this.convertValues(source["Book"], Book);
|
||||||
|
}
|
||||||
|
|
||||||
|
convertValues(a: any, classs: any, asMap: boolean = false): any {
|
||||||
|
if (!a) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
if (a.slice && a.map) {
|
||||||
|
return (a as any[]).map(elem => this.convertValues(elem, classs));
|
||||||
|
} else if ("object" === typeof a) {
|
||||||
|
if (asMap) {
|
||||||
|
for (const key of Object.keys(a)) {
|
||||||
|
a[key] = new classs(a[key]);
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
return new classs(a);
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class Book {
|
||||||
|
ID: number;
|
||||||
|
// Go type: time
|
||||||
|
CreatedAt: any;
|
||||||
|
// Go type: time
|
||||||
|
UpdatedAt: any;
|
||||||
|
// Go type: gorm
|
||||||
|
DeletedAt: any;
|
||||||
|
Title: string;
|
||||||
|
ISBN: string;
|
||||||
|
AuthorID: number;
|
||||||
|
Author: Author;
|
||||||
|
Lendings: Lending[];
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new Book(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) source = JSON.parse(source);
|
||||||
|
this.ID = source["ID"];
|
||||||
|
this.CreatedAt = this.convertValues(source["CreatedAt"], null);
|
||||||
|
this.UpdatedAt = this.convertValues(source["UpdatedAt"], null);
|
||||||
|
this.DeletedAt = this.convertValues(source["DeletedAt"], null);
|
||||||
|
this.Title = source["Title"];
|
||||||
|
this.ISBN = source["ISBN"];
|
||||||
|
this.AuthorID = source["AuthorID"];
|
||||||
|
this.Author = this.convertValues(source["Author"], Author);
|
||||||
|
this.Lendings = this.convertValues(source["Lendings"], Lending);
|
||||||
|
}
|
||||||
|
|
||||||
|
convertValues(a: any, classs: any, asMap: boolean = false): any {
|
||||||
|
if (!a) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
if (a.slice && a.map) {
|
||||||
|
return (a as any[]).map(elem => this.convertValues(elem, classs));
|
||||||
|
} else if ("object" === typeof a) {
|
||||||
|
if (asMap) {
|
||||||
|
for (const key of Object.keys(a)) {
|
||||||
|
a[key] = new classs(a[key]);
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
return new classs(a);
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class Author {
|
||||||
|
ID: number;
|
||||||
|
// Go type: time
|
||||||
|
CreatedAt: any;
|
||||||
|
// Go type: time
|
||||||
|
UpdatedAt: any;
|
||||||
|
// Go type: gorm
|
||||||
|
DeletedAt: any;
|
||||||
|
Name: string;
|
||||||
|
Books: Book[];
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new Author(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) source = JSON.parse(source);
|
||||||
|
this.ID = source["ID"];
|
||||||
|
this.CreatedAt = this.convertValues(source["CreatedAt"], null);
|
||||||
|
this.UpdatedAt = this.convertValues(source["UpdatedAt"], null);
|
||||||
|
this.DeletedAt = this.convertValues(source["DeletedAt"], null);
|
||||||
|
this.Name = source["Name"];
|
||||||
|
this.Books = this.convertValues(source["Books"], Book);
|
||||||
|
}
|
||||||
|
|
||||||
|
convertValues(a: any, classs: any, asMap: boolean = false): any {
|
||||||
|
if (!a) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
if (a.slice && a.map) {
|
||||||
|
return (a as any[]).map(elem => this.convertValues(elem, classs));
|
||||||
|
} else if ("object" === typeof a) {
|
||||||
|
if (asMap) {
|
||||||
|
for (const key of Object.keys(a)) {
|
||||||
|
a[key] = new classs(a[key]);
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
return new classs(a);
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
15
frontend/wailsjs/go/things/Service.d.ts
vendored
15
frontend/wailsjs/go/things/Service.d.ts
vendored
@ -1,15 +0,0 @@
|
|||||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
|
||||||
// This file is automatically generated. DO NOT EDIT
|
|
||||||
import {model} from '../models';
|
|
||||||
|
|
||||||
export function AddSubThing(arg1:number,arg2:string):Promise<void>;
|
|
||||||
|
|
||||||
export function DeleteSubThing(arg1:number):Promise<void>;
|
|
||||||
|
|
||||||
export function DeleteThing(arg1:number):Promise<void>;
|
|
||||||
|
|
||||||
export function GetSubThings(arg1:number):Promise<Array<model.SubThing>>;
|
|
||||||
|
|
||||||
export function GetThings():Promise<Array<model.Thing>>;
|
|
||||||
|
|
||||||
export function NewThing(arg1:string):Promise<void>;
|
|
@ -1,27 +0,0 @@
|
|||||||
// @ts-check
|
|
||||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
|
||||||
// This file is automatically generated. DO NOT EDIT
|
|
||||||
|
|
||||||
export function AddSubThing(arg1, arg2) {
|
|
||||||
return window['go']['things']['Service']['AddSubThing'](arg1, arg2);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function DeleteSubThing(arg1) {
|
|
||||||
return window['go']['things']['Service']['DeleteSubThing'](arg1);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function DeleteThing(arg1) {
|
|
||||||
return window['go']['things']['Service']['DeleteThing'](arg1);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function GetSubThings(arg1) {
|
|
||||||
return window['go']['things']['Service']['GetSubThings'](arg1);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function GetThings() {
|
|
||||||
return window['go']['things']['Service']['GetThings']();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function NewThing(arg1) {
|
|
||||||
return window['go']['things']['Service']['NewThing'](arg1);
|
|
||||||
}
|
|
9
main.go
9
main.go
@ -2,8 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"embed"
|
"embed"
|
||||||
"wails-template/model"
|
"library-manager/model"
|
||||||
"wails-template/things"
|
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2"
|
"github.com/wailsapp/wails/v2"
|
||||||
"github.com/wailsapp/wails/v2/pkg/options"
|
"github.com/wailsapp/wails/v2/pkg/options"
|
||||||
@ -15,13 +14,12 @@ var assets embed.FS
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// Create an instance of the app structure
|
// Create an instance of the app structure
|
||||||
app := NewApp()
|
|
||||||
db := model.InitDB()
|
db := model.InitDB()
|
||||||
things := &things.Service{DB: db}
|
app := NewApp(db)
|
||||||
|
|
||||||
// Create application with options
|
// Create application with options
|
||||||
err := wails.Run(&options.App{
|
err := wails.Run(&options.App{
|
||||||
Title: "wails-template",
|
Title: "library-manager",
|
||||||
Width: 1024,
|
Width: 1024,
|
||||||
Height: 768,
|
Height: 768,
|
||||||
AssetServer: &assetserver.Options{
|
AssetServer: &assetserver.Options{
|
||||||
@ -31,7 +29,6 @@ func main() {
|
|||||||
OnStartup: app.startup,
|
OnStartup: app.startup,
|
||||||
Bind: []interface{}{
|
Bind: []interface{}{
|
||||||
app,
|
app,
|
||||||
things,
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -4,21 +4,44 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"time"
|
||||||
|
|
||||||
"gorm.io/driver/sqlite"
|
"gorm.io/driver/sqlite"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Thing struct {
|
type Author struct {
|
||||||
ID int
|
gorm.Model
|
||||||
Name string
|
Name string
|
||||||
Subthings []SubThing
|
Books []Book
|
||||||
}
|
}
|
||||||
|
|
||||||
type SubThing struct {
|
type Book struct {
|
||||||
ID int
|
gorm.Model
|
||||||
ThingID int
|
Title string
|
||||||
Name string
|
ISBN string
|
||||||
|
AuthorID uint
|
||||||
|
Author Author `gorm:"foreignKey:AuthorID"`
|
||||||
|
Lendings []Lending `gorm:"foreignKey:BookID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
gorm.Model
|
||||||
|
Name string
|
||||||
|
Email string
|
||||||
|
Lendings []Lending `gorm:"foreignKey:ClientID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Lending struct {
|
||||||
|
gorm.Model
|
||||||
|
DueDate time.Time
|
||||||
|
Returned bool
|
||||||
|
|
||||||
|
ClientID uint
|
||||||
|
Client Client `gorm:"foreignKey:ClientID"`
|
||||||
|
|
||||||
|
BookID uint
|
||||||
|
Book Book `gorm:"foreignKey:BookID"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitDB() *gorm.DB {
|
func InitDB() *gorm.DB {
|
||||||
@ -26,10 +49,10 @@ func InitDB() *gorm.DB {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
db, err := gorm.Open(sqlite.Open(path.Join(home, "things.db")))
|
db, err := gorm.Open(sqlite.Open(path.Join(home, "library.db")))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Panic(err)
|
log.Panic(err)
|
||||||
}
|
}
|
||||||
db.AutoMigrate(&Thing{}, &SubThing{})
|
db.AutoMigrate(&Author{}, &Book{}, &Client{}, &Lending{})
|
||||||
return db
|
return db
|
||||||
}
|
}
|
||||||
|
@ -1,54 +0,0 @@
|
|||||||
package things
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"wails-template/model"
|
|
||||||
|
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Service struct {
|
|
||||||
DB *gorm.DB
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) NewThing(name string) {
|
|
||||||
if err := s.DB.Save(&model.Thing{Name: name}).Error; err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
print(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) GetThings() (things []model.Thing) {
|
|
||||||
if err := s.DB.Find(&things).Error; err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
return things
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) DeleteThing(id int) {
|
|
||||||
if err := s.DB.Delete(model.Thing{}, id).Error; err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) GetSubThings(thingID int) (subthings []model.SubThing) {
|
|
||||||
if err := s.DB.Where("thing_id = ?", thingID).Find(&subthings).Error; err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
func (s *Service) AddSubThing(thingID int, name string) {
|
|
||||||
if err := s.DB.Save(&model.SubThing{
|
|
||||||
ThingID: thingID,
|
|
||||||
Name: name,
|
|
||||||
}).Error; err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) DeleteSubThing(id int) {
|
|
||||||
if err := s.DB.Delete(model.SubThing{}, id).Error; err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
8
utils.go
Normal file
8
utils.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
func Greater(a, b uint) uint {
|
||||||
|
if a > b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://wails.io/schemas/config.v2.json",
|
"$schema": "https://wails.io/schemas/config.v2.json",
|
||||||
"name": "wails-template",
|
"name": "library-manager",
|
||||||
"outputfilename": "wails-template",
|
"outputfilename": "library-manager",
|
||||||
"frontend:install": "pnpm install",
|
"frontend:install": "pnpm install",
|
||||||
"frontend:build": "pnpm run build",
|
"frontend:build": "pnpm run build",
|
||||||
"frontend:dev:watcher": "pnpm run dev",
|
"frontend:dev:watcher": "pnpm run dev",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user