Skip to content

Commit

Permalink
23/equipment with size (#30)
Browse files Browse the repository at this point in the history
Can order equipment and convert an order to equipment

---------

Co-authored-by: Arne Maier <arne.maier@inovex.de>
  • Loading branch information
KordonDev and Arne Maier authored Aug 11, 2023
1 parent ddcc500 commit 36da091
Show file tree
Hide file tree
Showing 24 changed files with 990 additions and 32 deletions.
5 changes: 3 additions & 2 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import (

type Config struct {
Debug bool
DatebaseConnection string
DatabaseConnection string `yaml:"databaseConnection"`
Domain string
Origin string
JwtSecret string
JwtSecret string `yaml:"jwtSecret"`
}

func New(path string) (Config, error) {
Expand All @@ -25,5 +25,6 @@ func New(path string) (Config, error) {
if err := yaml.NewDecoder(configFile).Decode(&config); err != nil {
return Config{}, fmt.Errorf("failed to decode config file: %w", err)
}

return config, nil
}
1 change: 1 addition & 0 deletions configuration
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ debug: false
domain: localhost
origin: http://localhost:5173
jwtSecret: changeMe
databaseConnection: ./test.db
18 changes: 18 additions & 0 deletions equipment/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,24 @@ func (edb *equipmentDB) getFreeEquipment() ([]*models.Equipment, error) {
return listFormDB(dbEquipment), nil
}

func (edb *equipmentDB) save(equipment *models.Equipment) (*models.Equipment, error) {
e := equipment.ToDb()
err := edb.Save(&e).Error
if err != nil {
return nil, err
}
return e.FromDB(), nil
}

func (edb *equipmentDB) getByMemberIdAndType(memberId uint64, eType models.EquipmentType) (*models.Equipment, error) {
dbEquipment := models.DbEquipment{}
err := edb.Where("type = ? AND member_id = ?", eType, memberId).Find(&dbEquipment).Error
if err != nil {
return nil, err
}
return dbEquipment.FromDB(), err
}

func listFormDB(dbEquipment []models.DbEquipment) []*models.Equipment {
equipment := make([]*models.Equipment, 0)
for _, v := range dbEquipment {
Expand Down
43 changes: 42 additions & 1 deletion equipment/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import (
)

type EquipmentDatabase interface {
CreateEquipent(*models.Equipment) (*models.Equipment, error)
getById(uint64) (*models.Equipment, error)
getByType(string) ([]*models.Equipment, error)
CreateEquipent(*models.Equipment) (*models.Equipment, error)
delete(uint64) error
getAllByIds([]uint64) ([]*models.Equipment, error)
getFreeEquipment() ([]*models.Equipment, error)
save(*models.Equipment) (*models.Equipment, error)
getByMemberIdAndType(uint64, models.EquipmentType) (*models.Equipment, error)
}

type EquipmentService struct {
Expand Down Expand Up @@ -53,3 +55,42 @@ func (s EquipmentService) getFreeEquipment() (map[models.EquipmentType][]*models
}
return equipments, err
}

func (s EquipmentService) save(e models.Equipment) (*models.Equipment, error) {
return s.db.save(&e)
}

func (s EquipmentService) CreateEquipmentFromOrder(order models.Order, registrationCode string) (*models.Equipment, error) {
newEquipment := models.Equipment{
Id: 0,
Size: order.Size,
RegistrationCode: registrationCode,
Type: order.Type,
MemberID: order.MemberID,
}
return s.createEquipment(newEquipment)
}

func (s EquipmentService) ReplaceEquipmentForMember(equipment models.Equipment) (*models.Equipment, error) {
newEquipment, err := s.getEquipmentById(equipment.Id)
if err != nil {
return nil, err
}

oldEquipment, _ := s.db.getByMemberIdAndType(equipment.MemberID, equipment.Type)
if oldEquipment != nil {
oldEquipment.MemberID = 0
_, err := s.save(*oldEquipment)
if err != nil {
return nil, err
}
}

newEquipment.MemberID = equipment.MemberID
e, err := s.save(*newEquipment)
if err != nil {
return nil, err
}

return e, nil
}
10 changes: 9 additions & 1 deletion frontend/src/App.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script lang="ts">
import Router, { link, replace } from "svelte-spa-router";
import Router, { replace } from "svelte-spa-router";
import Login from "./views/security/Login.svelte";
import { routes } from "./routes";
import Register from "./views/security/Register.svelte";
Expand All @@ -12,6 +12,10 @@
import CreateEquipment from "./views/equipment/NewEquipment.svelte";
import EquipmentByType from "./views/equipment/EquipmentByType.svelte";
import EquipmentDetails from "./views/equipment/EquipmentDetails.svelte";
import Orders from "./views/order/Orders.svelte";
import OrderDetails from "./views/order/OrderDetails.svelte";
import AddOrder from "./views/order/NewOrder.svelte";
import FulfilledOrders from "./views/order/FulfilledOrders.svelte";
if (window.location.pathname === "/" && window.location.hash === "") {
replace(routes.MemberOverview.link);
Expand All @@ -30,6 +34,10 @@
[routes.EquipmentType.path]: EquipmentByType,
[routes.EquipmentAdd.path]: CreateEquipment,
[routes.EquipmentDetails.path]: EquipmentDetails,
[routes.Orders.path]: Orders,
[routes.OrdersFulfilled.path]: FulfilledOrders,
[routes.OrderDetails.path]: OrderDetails,
[routes.AddOrder.path]: AddOrder,
[routes.NotApproved.path]: NotApproved,
}}
/>
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/components/Navigation/Navigation.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
Ausrüstung
</a>
|
<a href={routes.Orders.link} use:link>Bestellungen</a>
|
<a href={routes.Users.link} use:link>User</a> |
<button on:click={logout}>Ausloggen</button>
</nav>
14 changes: 14 additions & 0 deletions frontend/src/components/timeHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const getDate = (date: string): Date | undefined => {
if (date === "0001-01-01T00:00:00Z") {
return undefined;
}
return new Date(date);
}

export const formatToDate = (date?: Date): string => {
if (!date) {
return "-";
}
return date.toLocaleDateString('de-de', { year: "numeric", month: "short", day: "numeric" });
}

18 changes: 16 additions & 2 deletions frontend/src/routes.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { EquipmentType } from "./views/equipment/equipment.service";

export const routes = {
Login: {
path: "/login",
Expand Down Expand Up @@ -37,6 +35,22 @@ export const routes = {
path: "/equipment/details/:id",
link: `/equipment/details/`,
},
Orders: {
path: "/orders/",
link: "/orders",
},
OrdersFulfilled: {
path: "/orders/fulfilled",
link: "/orders/fulfilled",
},
OrderDetails: {
path: "/orders/:id",
link: "/orders/",
},
AddOrder: {
path: "/orders/members/:memberId",
link: "/orders/members/",
},
NotApproved: {
path: "/not-approved",
link: "/not-approved",
Expand Down
1 change: 1 addition & 0 deletions frontend/src/views/equipment/equipment.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export const NoEquipment: Equipment = {
id: 0,
registrationCode: "Keine",
type: EquipmentType.TShirt,
size: ""
};

export function addNoneEquipment(equipments: EquipmentByType): EquipmentByType {
Expand Down
62 changes: 38 additions & 24 deletions frontend/src/views/member/MemberDetail.svelte
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
<script lang="ts">
import {
Button,
Label,
Modal,
Select,
Spinner,
Alert,
} from "flowbite-svelte";
import { Button, Modal, Spinner, Alert, Heading } from "flowbite-svelte";
import { routes } from "../../routes";
import { push } from "svelte-spa-router";
import { push, link } from "svelte-spa-router";
import {
errorNotification,
successNotification,
Expand All @@ -23,14 +16,16 @@
import MemberCard from "./MemberCard.svelte";
import Navigation from "../../components/Navigation/Navigation.svelte";
import { writable } from "svelte/store";
import OrderCard from "../order/OrderCard.svelte";
import { getOrdersForMember } from "../order/order.service"
export let params = { id: undefined };
const member = writable<Member | undefined>();
let loadingError = false;
getMember(params.id)
const memberPromise = getMember(params.id)
.then((m) => member.set(m))
.catch(() => (loadingError = true));
const ordersPromise = getOrdersForMember(params.id);
let deleteModalOpen = false;
let loading = false;
Expand Down Expand Up @@ -68,16 +63,36 @@

<Navigation />

{#if $member !== undefined}
<MemberCard member={$member} columns={6} />
{#await memberPromise}
<Spinner />
{:then}

<div class="mb-4">
<MemberCard member={$member} columns={6} />
<MemberForm
memberStore={member}
onSubmit={updateMemberInternal}
submitText="Speichern"
loading={false}
hideEquipment={false}
/>
</div>

<MemberForm
memberStore={member}
onSubmit={updateMemberInternal}
submitText="Speichern"
loading={false}
hideEquipment={false}
/>
<div class="mb-4">
<Heading tag="h3">Bestellungen</Heading>
<a href={`${routes.AddOrder.link}${params.id}`} use:link>Ausrüstung bestellen</a>
{#await ordersPromise }
<Spinner />
{:then orders}
<div class="flex flex-wrap">
{#each orders as order}
<OrderCard order={order} />
{:else}
<p>Keine offenen Bestellungen</p>
{/each}
</div>
{/await}
</div>

<Button color="red" on:click={() => (deleteModalOpen = true)}>
Mitglied löschen
Expand Down Expand Up @@ -105,7 +120,6 @@
{/if}
</svelte:fragment>
</Modal>
{/if}
{#if loadingError}
{:catch}
<Alert class="mb-4" color="red">Mitglied konnte nicht geladen werden.</Alert>
{/if}
{/await}
73 changes: 73 additions & 0 deletions frontend/src/views/order/FulfillOrderModal.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<script lang="ts">
import { fulfillOrder, type Order } from "./order.service";
import { Label, Input, Button, Modal, Spinner } from "flowbite-svelte";
import { createNotification } from "../../components/Notification/notificationStore";
import { push } from "svelte-spa-router";
import { routes } from "../../routes";
import { translateEquipmentType } from "../equipment/equipment.service";
export let order: Order;
let fulfillModalOpen = false;
let loading = false;
let registrationCode = "";
function fulfillOrder_internal() {
loading = true;
fulfillOrder(order, registrationCode)
.then((equipment) => {
createNotification(
{
color: "green",
text: `Bestellung wurde zu einem Ausrüstungsgegenstand.`,
},
5
);
loading = false;
fulfillModalOpen = false;
push(`${routes.MemberDetail.link}${equipment.memberId}`);
})
.catch(() => {
createNotification(
{
color: "red",
text: `Bestellung konnte nicht zu einem Ausrüstungsgegenstand gemacht und zugewiesen werden. Prüfe, ob es einen neuen Ausrüstungsgegenstand gibt und probiere sonst nocheinmal.`,
},
20
);
fulfillModalOpen = false;
loading = false;
});
}
</script>

<Button color="green" on:click={() => (fulfillModalOpen = true)}>
Bestellung einlösen
</Button>
<Modal
title={`Bestellung ${translateEquipmentType(order.type)} einlösen`}
bind:open={fulfillModalOpen}
>
<p>
Soll die Bestellung für {translateEquipmentType(order.type)} eingelöst werden?
</p>
<form on:submit|preventDefault={() => fulfillOrder_internal()} disabled={loading} id="fulfilOrderForm">
<Label for="registrationCode" class="block mb-2">Registrierungsnummer</Label>
<Input
required
class="mb-4"
id="registrationCode"
bind:value={registrationCode}
/>
</form>
<svelte:fragment slot="footer">
{#if loading}
<Spinner />
{:else}
<Button color="green" form="fulfilOrderForm" type="submit">
Einlösen
</Button>
<Button color="purple">Abbrechen</Button>
{/if}
</svelte:fragment>
</Modal>
28 changes: 28 additions & 0 deletions frontend/src/views/order/FulfilledOrders.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<script lang="ts">
import Navigation from "../../components/Navigation/Navigation.svelte";
import { Alert, Spinner } from "flowbite-svelte";
import { getFulfilledOrders } from "./order.service";
import OrderCard from "./OrderCard.svelte";
let orderPromise = getFulfilledOrders();
</script>

<Navigation />
<h1>Fertige Bestellungen</h1>

{#await orderPromise}
<Spinner />
{:then orders}
<div>
Aktuell {orders.length} Bestellungen fertig
<div class="flex flex-wrap">
{#each orders as order}
<OrderCard order={order} withMember={true} withDuration={true} />
{/each}
</div>
</div>
{:catch}
<Alert class="mb-4 top-alert" color="red" dismissable style="display: block;">
Leider konnten die Bestellungen nicht geladen werden.
</Alert>
{/await}
Loading

0 comments on commit 36da091

Please sign in to comment.