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
|
||||
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
101
app.go
@ -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
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,16 +1,19 @@
|
||||
<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">
|
||||
<div class="flex gap-5">
|
||||
<button
|
||||
class="grid grid-cols-3 items-center"
|
||||
onclick={() => navigate("/")}
|
||||
@ -18,8 +21,17 @@
|
||||
<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>
|
||||
|
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">
|
||||
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");
|
||||
|
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
|
||||
// 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>;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -1,35 +1,174 @@
|
||||
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"];
|
||||
this.Email = source["Email"];
|
||||
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 Thing {
|
||||
export class Lending {
|
||||
ID: number;
|
||||
Name: string;
|
||||
Subthings: SubThing[];
|
||||
// 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 Thing(source);
|
||||
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.Subthings = this.convertValues(source["Subthings"], SubThing);
|
||||
this.Books = this.convertValues(source["Books"], Book);
|
||||
}
|
||||
|
||||
convertValues(a: any, classs: any, asMap: boolean = false): any {
|
||||
@ -51,5 +190,7 @@ export namespace model {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
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 (
|
||||
"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,
|
||||
},
|
||||
})
|
||||
|
||||
|
@ -4,21 +4,44 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Thing struct {
|
||||
ID int
|
||||
type Author struct {
|
||||
gorm.Model
|
||||
Name string
|
||||
Subthings []SubThing
|
||||
Books []Book
|
||||
}
|
||||
|
||||
type SubThing struct {
|
||||
ID int
|
||||
ThingID int
|
||||
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
|
||||
}
|
||||
|
@ -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",
|
||||
"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",
|
||||
|
Loading…
x
Reference in New Issue
Block a user