management of library
All checks were successful
build / windows (push) Successful in 2m38s
build / linux (push) Successful in 2m0s

This commit is contained in:
schreifuchs 2025-03-07 11:51:21 +01:00
parent e90cdf4e6a
commit 5bedea5312
26 changed files with 1078 additions and 228 deletions

3
.timer.toml Normal file
View File

@ -0,0 +1,3 @@
## dates and their corresponding seconds been here :)
[25-03-07]
schreifuchs_at_archibald = 407

View File

@ -1,12 +1,8 @@
# wails template
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 :)
# Library Manager
übung für ICT-Regios 2025
zeit: 4h 15min
## Links

101
app.go
View File

@ -3,19 +3,22 @@ package main
import (
"context"
"fmt"
"library-manager/model"
"github.com/gen2brain/beeep"
"gorm.io/gorm"
)
// App struct
type App struct {
ctx context.Context
db *gorm.DB
}
// 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
@ -33,3 +36,97 @@ func (a *App) startup(ctx context.Context) {
func (a *App) Greet(name string) string {
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
View File

@ -0,0 +1,3 @@
## dates and their corresponding seconds been here :)
[25-03-07]
schreifuchs_at_archibald = 15333

View File

@ -1,25 +1,37 @@
<script lang="ts">
import "./app.css";
import { Router, Route, Link, navigate } from "svelte-routing";
import Home from "./routes/Home.svelte";
import "./app.css";
import { Navbar, DarkMode, Heading } from "flowbite-svelte";
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("/");
</script>
<main class="flex-col h-screen items-center bg-gray-50 dark:bg-gray-900">
<Router bind:url>
<Navbar class="border-b">
<button
class="grid grid-cols-3 items-center"
onclick={() => navigate("/")}
>
<HomeOutline />
<span class="col-span-2">HOME</span>
</button>
<div class="flex gap-5">
<button
class="grid grid-cols-3 items-center"
onclick={() => navigate("/")}
>
<HomeOutline />
<span class="col-span-2">HOME</span>
</button>
<Link to="/clients">Clients</Link>
<Link to="/books">Books</Link>
</div>
<DarkMode />
</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>
</main>

View 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>

View 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>

View 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>

View 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>

View File

@ -1,7 +1,7 @@
<script lang="ts">
import { Input } from "flowbite-svelte";
let { value }: { value: Date } = $props();
let { value = $bindable() }: { value: Date } = $props();
const formatDateTimeLocal = (date: Date) => {
const pad = (num) => num.toString().padStart(2, "0");

View 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>

View 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>

View 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>

View File

@ -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>

View 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>

View File

@ -1,4 +1,39 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// 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 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>;

View File

@ -2,6 +2,74 @@
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
// 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) {
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);
}

View File

@ -1,35 +1,30 @@
export namespace model {
export class SubThing {
export class Client {
ID: number;
ThingID: number;
// Go type: time
CreatedAt: any;
// Go type: time
UpdatedAt: any;
// Go type: gorm
DeletedAt: any;
Name: string;
Email: string;
Lendings: Lending[];
static createFrom(source: any = {}) {
return new SubThing(source);
return new Client(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
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"];
}
}
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);
this.Email = source["Email"];
this.Lendings = this.convertValues(source["Lendings"], Lending);
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
@ -50,6 +45,152 @@ export namespace model {
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;
}
}
}

View File

@ -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>;

View File

@ -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);
}

2
go.mod
View File

@ -1,4 +1,4 @@
module wails-template
module library-manager
go 1.24.0

View File

@ -2,8 +2,7 @@ package main
import (
"embed"
"wails-template/model"
"wails-template/things"
"library-manager/model"
"github.com/wailsapp/wails/v2"
"github.com/wailsapp/wails/v2/pkg/options"
@ -15,13 +14,12 @@ var assets embed.FS
func main() {
// Create an instance of the app structure
app := NewApp()
db := model.InitDB()
things := &things.Service{DB: db}
app := NewApp(db)
// Create application with options
err := wails.Run(&options.App{
Title: "wails-template",
Title: "library-manager",
Width: 1024,
Height: 768,
AssetServer: &assetserver.Options{
@ -31,7 +29,6 @@ func main() {
OnStartup: app.startup,
Bind: []interface{}{
app,
things,
},
})

View File

@ -4,21 +4,44 @@ import (
"log"
"os"
"path"
"time"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
type Thing struct {
ID int
Name string
Subthings []SubThing
type Author struct {
gorm.Model
Name string
Books []Book
}
type SubThing struct {
ID int
ThingID int
Name string
type Book struct {
gorm.Model
Title 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 {
@ -26,10 +49,10 @@ func InitDB() *gorm.DB {
if err != nil {
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 {
log.Panic(err)
}
db.AutoMigrate(&Thing{}, &SubThing{})
db.AutoMigrate(&Author{}, &Book{}, &Client{}, &Lending{})
return db
}

View File

@ -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
View File

@ -0,0 +1,8 @@
package main
func Greater(a, b uint) uint {
if a > b {
return a
}
return b
}

View File

@ -1,7 +1,7 @@
{
"$schema": "https://wails.io/schemas/config.v2.json",
"name": "wails-template",
"outputfilename": "wails-template",
"name": "library-manager",
"outputfilename": "library-manager",
"frontend:install": "pnpm install",
"frontend:build": "pnpm run build",
"frontend:dev:watcher": "pnpm run dev",