Update. Add svelte admin frontend
This commit is contained in:
24
web/frontend/.gitignore
vendored
24
web/frontend/.gitignore
vendored
@@ -1,24 +0,0 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
@@ -1,45 +1,139 @@
|
||||
<script lang="ts">
|
||||
import svelteLogo from "./assets/svelte.svg";
|
||||
import Counter from "./lib/Counter.svelte";
|
||||
import Table from "./lib/Table.svelte";
|
||||
import GenericForm from "./lib/GenericForm.svelte";
|
||||
|
||||
type Retailer = {
|
||||
id: number;
|
||||
shopname: string;
|
||||
url: string;
|
||||
country: string;
|
||||
display: number;
|
||||
};
|
||||
|
||||
let records: Retailer[] = $state([]);
|
||||
let columns: string[] = ["shopname", "country", "url"];
|
||||
let activeTab = $state("invoice");
|
||||
|
||||
async function fetchData(endpoint: string) {
|
||||
const res = await fetch("http://localhost:8080/api/" + endpoint);
|
||||
records = await res.json();
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
if (activeTab === "retailers") {
|
||||
fetchData("retailers/");
|
||||
}
|
||||
});
|
||||
|
||||
let showModal = $state(false);
|
||||
|
||||
function openModal() {
|
||||
showModal = true;
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
showModal = false;
|
||||
}
|
||||
|
||||
let formdata: Retailer = {
|
||||
shopname: "",
|
||||
url: "",
|
||||
id: 0,
|
||||
country: "",
|
||||
display: 0,
|
||||
};
|
||||
|
||||
function submitUserForm(data: Retailer) {
|
||||
closeModal();
|
||||
console.log("User Form Submitted:", data);
|
||||
}
|
||||
function removeRowHandler(id: Number) {
|
||||
console.log("Remove Record:", id);
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Bootstrap Modal -->
|
||||
<div
|
||||
class="modal fade {showModal ? 'show' : ''}"
|
||||
tabindex="-1"
|
||||
role="dialog"
|
||||
style="display: {showModal ? 'block' : 'none'};"
|
||||
>
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Add Retailer</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<GenericForm formData={formdata} onSubmit={submitUserForm}>
|
||||
{#each columns as col}
|
||||
<div class="mb-3">
|
||||
<label for={col}>{col}:</label>
|
||||
<input
|
||||
id={col}
|
||||
type="text"
|
||||
bind:value={formdata[col as keyof Retailer]}
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
</GenericForm>
|
||||
</div>
|
||||
<div class="modal-footer justify-content-end">
|
||||
<button type="button" class="btn btn-secondary" onclick={closeModal}
|
||||
>Close</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<main>
|
||||
<div>
|
||||
<a href="https://svelte.dev" target="_blank" rel="noreferrer">
|
||||
<img src={svelteLogo} class="logo svelte" alt="Svelte Logo" />
|
||||
</a>
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="nav-item">
|
||||
<a
|
||||
class="nav-link {activeTab === 'invoice' ? 'active' : ''}"
|
||||
href="#"
|
||||
onclick={() => (activeTab = "invoice")}>Invoices</a
|
||||
>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a
|
||||
class="nav-link {activeTab === 'retailers' ? 'active' : ''}"
|
||||
href="#"
|
||||
onclick={() => (activeTab = "retailers")}>Retailers</a
|
||||
>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a
|
||||
class="nav-link {activeTab === 'news' ? 'active' : ''}"
|
||||
href="#"
|
||||
onclick={() => (activeTab = "news")}>News</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="container-fluid">
|
||||
{#if activeTab === "invoice"}
|
||||
<p>Invoice list</p>
|
||||
{/if}
|
||||
{#if activeTab === "retailers"}
|
||||
<div class="row justify-content-start">
|
||||
<div class="col-2 mt-3">
|
||||
<button
|
||||
onclick={openModal}
|
||||
type="button"
|
||||
class="btn btn-outline-primary">Add retailer</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<Table {columns} {records} {removeRowHandler} />
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#if activeTab === "news"}
|
||||
<p>news</p>
|
||||
{/if}
|
||||
</div>
|
||||
<h1>Vite + Svelte</h1>
|
||||
|
||||
<div class="card">
|
||||
<Counter />
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Check out <a
|
||||
href="https://github.com/sveltejs/kit#readme"
|
||||
target="_blank"
|
||||
rel="noreferrer">SvelteKit</a
|
||||
>, the official Svelte app framework powered by Vite!
|
||||
</p>
|
||||
|
||||
<p class="read-the-docs">Click on the Vite and Svelte logos to learn more</p>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
.logo {
|
||||
height: 6em;
|
||||
padding: 1.5em;
|
||||
will-change: filter;
|
||||
transition: filter 300ms;
|
||||
}
|
||||
.logo:hover {
|
||||
filter: drop-shadow(0 0 2em #646cffaa);
|
||||
}
|
||||
.logo.svelte:hover {
|
||||
filter: drop-shadow(0 0 2em #ff3e00aa);
|
||||
}
|
||||
.read-the-docs {
|
||||
color: #888;
|
||||
}
|
||||
</style>
|
||||
|
14
web/frontend/src/lib/GenericForm.svelte
Normal file
14
web/frontend/src/lib/GenericForm.svelte
Normal file
@@ -0,0 +1,14 @@
|
||||
<script lang="ts" generics="T">
|
||||
export let formData: T;
|
||||
export let onSubmit: (data: T) => void;
|
||||
|
||||
function handleSubmit(event: SubmitEvent) {
|
||||
event.preventDefault();
|
||||
onSubmit(formData);
|
||||
}
|
||||
</script>
|
||||
|
||||
<form on:submit|preventDefault={handleSubmit}>
|
||||
<slot />
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
48
web/frontend/src/lib/Table.svelte
Normal file
48
web/frontend/src/lib/Table.svelte
Normal file
@@ -0,0 +1,48 @@
|
||||
<script lang="ts">
|
||||
interface Props {
|
||||
columns: string[];
|
||||
records: any;
|
||||
removeRowHandler: (id: number) => void;
|
||||
}
|
||||
|
||||
let { columns, records, removeRowHandler }: Props = $props();
|
||||
</script>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
{#each columns as col}
|
||||
<th scope="col">{col}</th>
|
||||
{/each}
|
||||
<th scope="col">edit</th>
|
||||
<th scope="col">remove</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each records as row}
|
||||
<tr>
|
||||
{#each columns as col}
|
||||
<td>{row[col]}</td>
|
||||
{/each}
|
||||
<td>
|
||||
<button
|
||||
onclick={() => removeRowHandler(row.id)}
|
||||
class="btn btn-outline-success align-items-center"
|
||||
aria-label="Edit record"
|
||||
>
|
||||
<i class="bi bi-pencil"></i>
|
||||
</button>
|
||||
</td>
|
||||
<td>
|
||||
<button
|
||||
onclick={() => removeRowHandler(row.id)}
|
||||
class="btn btn-outline-danger align-items-center"
|
||||
aria-label="Remove record"
|
||||
>
|
||||
<i class="bi bi-x-lg"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
@@ -4,4 +4,7 @@ import { svelte } from '@sveltejs/vite-plugin-svelte'
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [svelte()],
|
||||
build: {
|
||||
manifest: true
|
||||
}
|
||||
})
|
||||
|
@@ -2,6 +2,8 @@ package web
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"text/template"
|
||||
|
||||
@@ -19,9 +21,17 @@ type PageData struct {
|
||||
//go:embed templates
|
||||
var Templates embed.FS
|
||||
|
||||
//go:embed frontend/dist
|
||||
//go:embed all:frontend/dist
|
||||
var StaticAssets embed.FS
|
||||
|
||||
func DistFS() fs.FS {
|
||||
efs, err := fs.Sub(StaticAssets, "frontend/dist")
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("unable to serve frontend: %v", err))
|
||||
}
|
||||
return efs
|
||||
}
|
||||
|
||||
func RenderTemplate(w http.ResponseWriter, name string, data PageData) error {
|
||||
tpl := template.Must(template.ParseFS(Templates, "templates/"+name+".html", "templates/base.html"))
|
||||
return tpl.ExecuteTemplate(w, "base", data)
|
||||
|
@@ -1,3 +1,3 @@
|
||||
{{define "vite"}} {{ .Vite.Tags }} {{end}} {{define "content"}}
|
||||
<div id="root"></div>
|
||||
<div id="app"></div>
|
||||
{{end}}
|
||||
|
Reference in New Issue
Block a user