mirror of
https://github.com/Damillora/Shioriko.git
synced 2024-12-22 16:03:45 +00:00
feat: functioning upload frontend
This commit is contained in:
parent
dbff5649d7
commit
f04575bc5c
@ -14,7 +14,7 @@ func InitializeFrontendRoutes(g *gin.Engine) {
|
||||
func frontendHome(c *gin.Context) {
|
||||
baseURL := config.CurrentConfig.BaseURL
|
||||
c.HTML(http.StatusOK, "index.html", gin.H{
|
||||
"title": "Home",
|
||||
"title": "Shioriko",
|
||||
"base_url": baseURL,
|
||||
})
|
||||
}
|
||||
|
@ -14,24 +14,35 @@ func GetTagAll() []database.Tag {
|
||||
return tags
|
||||
}
|
||||
|
||||
func CreateOrUpdateTag(tagSyntax string) (*database.Tag, error) {
|
||||
tagFields := strings.Split(tagSyntax, ":")
|
||||
var tagName string
|
||||
var tagType database.TagType
|
||||
if len(tagFields) == 1 {
|
||||
tagName = tagFields[0]
|
||||
func CreateOrUpdateTagGeneric(tagName string) (*database.Tag, error) {
|
||||
var tag database.Tag
|
||||
result := database.DB.Where("name = ?", tagName).First(&tag)
|
||||
if result.Error != nil {
|
||||
var tagType database.TagType
|
||||
database.DB.Where("name = ?", "general").First(&tagType)
|
||||
} else if len(tagFields) == 2 {
|
||||
tagName = tagFields[1]
|
||||
result := database.DB.Where("name = ?", tagFields[0]).First(&tagType)
|
||||
|
||||
tag = database.Tag{
|
||||
ID: uuid.NewString(),
|
||||
Name: tagName,
|
||||
TagTypeID: tagType.ID,
|
||||
}
|
||||
result = database.DB.Create(&tag)
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
} else {
|
||||
return nil, errors.New("Malformed tag syntax")
|
||||
|
||||
}
|
||||
return &tag, nil
|
||||
}
|
||||
func CreateOrUpdateTagComplex(tagName string, tagTypeString string) (*database.Tag, error) {
|
||||
var tag database.Tag
|
||||
result := database.DB.Where("name = ? AND tag_type_id = ? ", tagName, tagType.ID).First(&tag)
|
||||
var tagType database.TagType
|
||||
result := database.DB.Where("name = ?", tagTypeString).First(&tagType)
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
|
||||
result = database.DB.Where("name = ? AND tag_type_id = ? ", tagName, tagType.ID).First(&tag)
|
||||
|
||||
if result.Error != nil {
|
||||
tag = database.Tag{
|
||||
@ -47,6 +58,21 @@ func CreateOrUpdateTag(tagSyntax string) (*database.Tag, error) {
|
||||
}
|
||||
return &tag, nil
|
||||
}
|
||||
func CreateOrUpdateTag(tagSyntax string) (*database.Tag, error) {
|
||||
tagFields := strings.Split(tagSyntax, ":")
|
||||
var tagName string
|
||||
var tagType string
|
||||
if len(tagFields) == 1 {
|
||||
tagName = tagFields[0]
|
||||
return CreateOrUpdateTagGeneric(tagName)
|
||||
} else if len(tagFields) == 2 {
|
||||
tagType = tagFields[0]
|
||||
tagName = tagFields[1]
|
||||
return CreateOrUpdateTagComplex(tagName, tagType)
|
||||
} else {
|
||||
return nil, errors.New("Malformed tag syntax")
|
||||
}
|
||||
}
|
||||
|
||||
func GetTag(tagSyntax string) (*database.Tag, error) {
|
||||
tagFields := strings.Split(tagSyntax, ":")
|
||||
|
@ -18,8 +18,14 @@
|
||||
"svelte": "^3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.21.1",
|
||||
"bulma": "^0.9.2",
|
||||
"node-sass": "^6.0.0",
|
||||
"postcss": "^8.2.14",
|
||||
"query-string": "^7.0.0",
|
||||
"sirv-cli": "^1.0.0",
|
||||
"svelte-routing": "^1.6.0"
|
||||
"svelte-preprocess": "^4.7.3",
|
||||
"svelte-routing": "^1.6.0",
|
||||
"svelte-tags-input": "^2.7.1"
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import resolve from '@rollup/plugin-node-resolve';
|
||||
import livereload from 'rollup-plugin-livereload';
|
||||
import { terser } from 'rollup-plugin-terser';
|
||||
import css from 'rollup-plugin-css-only';
|
||||
import sveltePreprocess from 'svelte-preprocess'
|
||||
|
||||
const production = !process.env.ROLLUP_WATCH;
|
||||
|
||||
@ -34,18 +35,30 @@ export default {
|
||||
sourcemap: true,
|
||||
format: 'iife',
|
||||
name: 'app',
|
||||
file: '../static/bundle.js'
|
||||
dir: "../static",
|
||||
assetFileNames: 'bundle.css',
|
||||
entryFileNames: 'bundle.js'
|
||||
},
|
||||
plugins: [
|
||||
svelte({
|
||||
compilerOptions: {
|
||||
// enable run-time checks when not in production
|
||||
dev: !production
|
||||
}
|
||||
},
|
||||
|
||||
preprocess: sveltePreprocess({
|
||||
sourceMap: !production,
|
||||
scss: {
|
||||
includePaths: [
|
||||
'node_modules',
|
||||
'src'
|
||||
]
|
||||
},
|
||||
}),
|
||||
}),
|
||||
// we'll extract any component CSS out into
|
||||
// a separate file - better for performance
|
||||
css({ output: '../static/bundle.css' }),
|
||||
css({ name: "bundle" }),
|
||||
|
||||
// If you have external dependencies installed from
|
||||
// npm, you'll most likely need these plugins. In
|
||||
|
@ -9,12 +9,10 @@
|
||||
import Login from "./routes/Login.svelte";
|
||||
import Logout from "./routes/Logout.svelte";
|
||||
import Tag from "./routes/Tag.svelte";
|
||||
|
||||
|
||||
import Upload from "./routes/Upload.svelte";
|
||||
|
||||
export let url = "";
|
||||
let baseURL = window.BASE_URL;
|
||||
|
||||
</script>
|
||||
|
||||
<Router {url}>
|
||||
@ -26,5 +24,25 @@
|
||||
<Route path="/post/:id" component={Post} />
|
||||
<Route path="/auth/login" component={Login} />
|
||||
<Route path="/auth/logout" component={Logout} />
|
||||
<Route path="/upload" component={Upload} />
|
||||
</div>
|
||||
</Router>
|
||||
|
||||
<style global lang="scss">
|
||||
@import "./main.scss";
|
||||
|
||||
#tags .svelte-tags-input-tag {
|
||||
background: $primary;
|
||||
color: $text-invert;
|
||||
}
|
||||
|
||||
#tags .svelte-tags-input-layout {
|
||||
@extend .input;
|
||||
height: inherit;
|
||||
|
||||
& .svelte-tags-input {
|
||||
margin-top: 0 !important;
|
||||
font-size: 13.3333px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
106
web/app/src/PostPaginator.svelte
Normal file
106
web/app/src/PostPaginator.svelte
Normal file
@ -0,0 +1,106 @@
|
||||
<script>
|
||||
import { Link } from "svelte-routing";
|
||||
|
||||
export let posts = [];
|
||||
export let page = 1;
|
||||
export let totalPages = 1;
|
||||
export let handlePage = (i) => { };
|
||||
export let url = "/posts";
|
||||
</script>
|
||||
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<nav class="pagination" role="navigation" aria-label="pagination">
|
||||
{#if page > 1}
|
||||
<Link
|
||||
on:click={handlePage(page - 1)}
|
||||
to="{url}?page={page - 1}"
|
||||
class="pagination-previous"
|
||||
aria-label="Previous">Previous</Link
|
||||
>
|
||||
{/if}
|
||||
{#if page < totalPages}
|
||||
<Link
|
||||
on:click={handlePage(page + 1)}
|
||||
to="{url}?page={page + 1}"
|
||||
class="pagination-next"
|
||||
aria-label="Next">Next</Link
|
||||
>
|
||||
{/if}
|
||||
<ul class="pagination-list">
|
||||
{#if page > 3}
|
||||
<li>
|
||||
<Link
|
||||
on:click={handlePage(1)}
|
||||
to="{url}?page={1}"
|
||||
class="pagination-link"
|
||||
aria-label="Goto page 1">1</Link
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<span class="pagination-ellipsis">…</span>
|
||||
</li>
|
||||
{/if}
|
||||
{#each [...Array(5).keys()].map((x) => x + page - 2) as i}
|
||||
{#if i >= 1 && i <= totalPages}
|
||||
{#if i == page}
|
||||
<li>
|
||||
<Link
|
||||
on:click={handlePage(i)}
|
||||
to="{url}?page={i}"
|
||||
class="pagination-link is-current"
|
||||
aria-label="Goto page {i}">{i}</Link
|
||||
>
|
||||
</li>
|
||||
{:else}
|
||||
<li>
|
||||
<Link
|
||||
on:click={handlePage(i)}
|
||||
to="{url}?page={i}"
|
||||
class="pagination-link"
|
||||
aria-label="Goto page {i}">{i}</Link
|
||||
>
|
||||
</li>
|
||||
{/if}
|
||||
{/if}
|
||||
{/each}
|
||||
{#if totalPages - page > 2}
|
||||
<li>
|
||||
<span class="pagination-ellipsis">…</span>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
on:click={handlePage(totalPages)}
|
||||
to="{url}?page={totalPages}"
|
||||
class="pagination-link"
|
||||
aria-label="Goto page {totalPages}"
|
||||
>{totalPages}</Link
|
||||
>
|
||||
</li>
|
||||
{/if}
|
||||
</ul>
|
||||
</nav>
|
||||
<div class="columns is-multiline">
|
||||
{#each posts as post (post.id)}
|
||||
<div class="column is-one-quarter card">
|
||||
<div class="card-image">
|
||||
<figure class="image">
|
||||
<Link to="/post/{post.id}">
|
||||
<img alt={post.id} src={post.image_path} />
|
||||
</Link>
|
||||
</figure>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="content">
|
||||
{#each post.tags as tag (tag)}
|
||||
<p>
|
||||
<Link to="/tag/{tag}">{tag}</Link>
|
||||
</p>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
@ -1,41 +1,78 @@
|
||||
import { token } from "./stores.js"
|
||||
import axios from "axios";
|
||||
|
||||
let url = window.BASE_URL;
|
||||
let current_token;
|
||||
const unsub_token = token.subscribe(value => {
|
||||
current_token = token;
|
||||
token.subscribe(value => {
|
||||
current_token = value;
|
||||
})
|
||||
|
||||
export async function login({ username, password }) {
|
||||
const endpoint = url + "/api/auth/login";
|
||||
const response = await fetch(endpoint, {
|
||||
const response = await axios({
|
||||
url: endpoint,
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
data: JSON.stringify({
|
||||
username,
|
||||
password,
|
||||
}),
|
||||
})
|
||||
const data = await response.json();
|
||||
token.set(data.token);
|
||||
return data;
|
||||
token.set(response.data.token);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
export async function getPosts({ page }) {
|
||||
const endpoint = url + "/api/post?page=" + page;
|
||||
const response = await fetch(endpoint);
|
||||
const data = await response.json();
|
||||
return data;
|
||||
const response = await axios.get(endpoint);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
export async function getPostsTag({ page, tag }) {
|
||||
const endpoint = url + "/api/post/tag/" + tag + "?page=" + page;
|
||||
const response = await fetch(endpoint);
|
||||
const data = await response.json();
|
||||
return data;
|
||||
const response = await axios(endpoint);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
export async function getPost({ id }) {
|
||||
const endpoint = url + "/api/post/" + id;
|
||||
const response = await fetch(endpoint);
|
||||
const data = await response.json();
|
||||
return data;
|
||||
const response = await axios(endpoint);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
export async function uploadBlob({ file, onProgress }) {
|
||||
var formData = new FormData();
|
||||
formData.append("file", file);
|
||||
const endpoint = url + "/api/blob/upload";
|
||||
const response = await axios({
|
||||
url: endpoint,
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Authorization': 'Bearer ' + current_token,
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
withCredentials: true,
|
||||
data: formData,
|
||||
onUploadProgress: e => {
|
||||
if (onProgress) {
|
||||
onProgress(e)
|
||||
}
|
||||
}
|
||||
})
|
||||
return response.data;
|
||||
}
|
||||
|
||||
export async function postCreate({ blob_id, source_url, tags }) {
|
||||
const endpoint = url + "/api/post/create";
|
||||
const response = await axios({
|
||||
url: endpoint,
|
||||
method: "POST",
|
||||
headers: {
|
||||
'Authorization': 'Bearer ' + current_token,
|
||||
},
|
||||
withCredentials: true,
|
||||
data: {
|
||||
blob_id, source_url, tags
|
||||
}
|
||||
})
|
||||
return response.data;
|
||||
}
|
9
web/app/src/main.scss
Normal file
9
web/app/src/main.scss
Normal file
@ -0,0 +1,9 @@
|
||||
|
||||
@import "../node_modules/bulma/sass/utilities/_all";
|
||||
@import "../node_modules/bulma/sass/base/_all";
|
||||
@import "../node_modules/bulma/sass/elements/_all";
|
||||
@import "../node_modules/bulma/sass/form/_all";
|
||||
@import "../node_modules/bulma/sass/components/_all";
|
||||
@import "../node_modules/bulma/sass/grid/_all";
|
||||
@import "../node_modules/bulma/sass/helpers/_all";
|
||||
@import "../node_modules/bulma/sass/layout/_all";
|
@ -3,6 +3,7 @@
|
||||
import { getPosts } from "../api.js";
|
||||
import { Link } from "svelte-routing";
|
||||
import queryString from "query-string";
|
||||
import PostPaginator from "../PostPaginator.svelte";
|
||||
|
||||
export let location;
|
||||
|
||||
@ -39,99 +40,4 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<nav class="pagination" role="navigation" aria-label="pagination">
|
||||
{#if page > 1}
|
||||
<Link
|
||||
on:click={handlePage(page - 1)}
|
||||
to="/posts?page={page - 1}"
|
||||
class="pagination-previous"
|
||||
aria-label="Previous">Previous</Link
|
||||
>
|
||||
{/if}
|
||||
{#if page < totalPages}
|
||||
<Link
|
||||
on:click={handlePage(page + 1)}
|
||||
to="/posts?page={page + 1}"
|
||||
class="pagination-next"
|
||||
aria-label="Next">Next</Link
|
||||
>
|
||||
{/if}
|
||||
<ul class="pagination-list">
|
||||
{#if page > 3}
|
||||
<li>
|
||||
<Link
|
||||
on:click={handlePage(1)}
|
||||
to="/posts?page={1}"
|
||||
class="pagination-link"
|
||||
aria-label="Goto page 1">1</Link
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<span class="pagination-ellipsis">…</span>
|
||||
</li>
|
||||
{/if}
|
||||
{#each [...Array(5).keys()].map((x) => x + page - 2) as i}
|
||||
{#if i >= 1 && i <= totalPages}
|
||||
{#if i == page}
|
||||
<li>
|
||||
<Link
|
||||
on:click={handlePage(i)}
|
||||
to="/posts?page={i}"
|
||||
class="pagination-link is-current"
|
||||
aria-label="Goto page {i}">{i}</Link
|
||||
>
|
||||
</li>
|
||||
{:else}
|
||||
<li>
|
||||
<Link
|
||||
on:click={handlePage(i)}
|
||||
to="/posts?page={i}"
|
||||
class="pagination-link"
|
||||
aria-label="Goto page {i}">{i}</Link
|
||||
>
|
||||
</li>
|
||||
{/if}
|
||||
{/if}
|
||||
{/each}
|
||||
{#if totalPages - page > 2}
|
||||
<li>
|
||||
<span class="pagination-ellipsis">…</span>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
on:click={handlePage(totalPages)}
|
||||
to="/posts?page={totalPages}"
|
||||
class="pagination-link"
|
||||
aria-label="Goto page {totalPages}"
|
||||
>{totalPages}</Link
|
||||
>
|
||||
</li>
|
||||
{/if}
|
||||
</ul>
|
||||
</nav>
|
||||
<div class="columns is-multiline">
|
||||
{#each posts as post (post.id)}
|
||||
<div class="column is-one-quarter card">
|
||||
<div class="card-image">
|
||||
<figure class="image">
|
||||
<Link to="/post/{post.id}">
|
||||
<img alt={post.id} src={post.image_path} />
|
||||
</Link>
|
||||
</figure>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="content">
|
||||
{#each post.tags as tag (tag)}
|
||||
<p>
|
||||
<Link to="/tag/{tag}">{tag}</Link>
|
||||
</p>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<PostPaginator url="/posts" posts={posts} page={page} totalPages={totalPages} handlePage={handlePage} />
|
@ -1,7 +1,7 @@
|
||||
<script>
|
||||
import { onMount } from "svelte";
|
||||
import { getPostsTag } from "../api.js";
|
||||
import { Link } from "svelte-routing";
|
||||
import PostPaginator from "../PostPaginator.svelte";
|
||||
import queryString from "query-string";
|
||||
|
||||
export let location;
|
||||
@ -44,99 +44,4 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<nav class="pagination" role="navigation" aria-label="pagination">
|
||||
{#if page > 1}
|
||||
<Link
|
||||
on:click={handlePage(page - 1)}
|
||||
to="/tag/{id}?page={page - 1}"
|
||||
class="pagination-previous"
|
||||
aria-label="Previous">Previous</Link
|
||||
>
|
||||
{/if}
|
||||
{#if page < totalPages}
|
||||
<Link
|
||||
on:click={handlePage(page + 1)}
|
||||
to="/tag/{id}?page={page + 1}"
|
||||
class="pagination-next"
|
||||
aria-label="Next">Next</Link
|
||||
>
|
||||
{/if}
|
||||
<ul class="pagination-list">
|
||||
{#if page > 3}
|
||||
<li>
|
||||
<Link
|
||||
on:click={handlePage(1)}
|
||||
to="/tag/{id}?page={1}"
|
||||
class="pagination-link"
|
||||
aria-label="Goto page 1">1</Link
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<span class="pagination-ellipsis">…</span>
|
||||
</li>
|
||||
{/if}
|
||||
{#each [...Array(5).keys()].map((x) => x + page - 2) as i}
|
||||
{#if i >= 1 && i <= totalPages}
|
||||
{#if i == page}
|
||||
<li>
|
||||
<Link
|
||||
on:click={handlePage(i)}
|
||||
to="/tag/{id}?page={i}"
|
||||
class="pagination-link is-current"
|
||||
aria-label="Goto page {i}">{i}</Link
|
||||
>
|
||||
</li>
|
||||
{:else}
|
||||
<li>
|
||||
<Link
|
||||
on:click={handlePage(i)}
|
||||
to="/tag/{id}?page={i}"
|
||||
class="pagination-link"
|
||||
aria-label="Goto page {i}">{i}</Link
|
||||
>
|
||||
</li>
|
||||
{/if}
|
||||
{/if}
|
||||
{/each}
|
||||
{#if totalPages - page > 2}
|
||||
<li>
|
||||
<span class="pagination-ellipsis">…</span>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
on:click={handlePage(totalPages)}
|
||||
to="/tag/{id}?page={totalPages}"
|
||||
class="pagination-link"
|
||||
aria-label="Goto page {totalPages}"
|
||||
>{totalPages}</Link
|
||||
>
|
||||
</li>
|
||||
{/if}
|
||||
</ul>
|
||||
</nav>
|
||||
<div class="columns is-multiline">
|
||||
{#each posts as post (post.id)}
|
||||
<div class="column is-one-quarter card">
|
||||
<div class="card-image">
|
||||
<figure class="image">
|
||||
<Link to="/post/{post.id}">
|
||||
<img alt={post.id} src={post.image_path} />
|
||||
</Link>
|
||||
</figure>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="content">
|
||||
{#each post.tags as tag (tag)}
|
||||
<p>
|
||||
<Link to="/tag/{tag}">{tag}</Link>
|
||||
</p>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<PostPaginator url="/tag/{id}" posts={posts} page={page} totalPages={totalPages} handlePage={handlePage} />
|
99
web/app/src/routes/Upload.svelte
Normal file
99
web/app/src/routes/Upload.svelte
Normal file
@ -0,0 +1,99 @@
|
||||
<script>
|
||||
import { uploadBlob, postCreate } from "../api.js";
|
||||
import { navigate } from "svelte-routing";
|
||||
import Tags from "svelte-tags-input";
|
||||
|
||||
let currentProgress = 0;
|
||||
|
||||
let fileName = "";
|
||||
|
||||
let form = {
|
||||
blob_id: "",
|
||||
source_url: "",
|
||||
tags: [],
|
||||
};
|
||||
|
||||
const onProgress = (e) => {
|
||||
var percentCompleted = Math.round((e.loaded * 100) / e.total);
|
||||
currentProgress = percentCompleted;
|
||||
};
|
||||
|
||||
const onFileChange = async (e) => {
|
||||
fileName = "";
|
||||
var file = e.target.files[0];
|
||||
if (file) {
|
||||
var response = await uploadBlob({ file, onProgress });
|
||||
form.blob_id = response.id;
|
||||
fileName = file.name;
|
||||
}
|
||||
};
|
||||
|
||||
const onTagChange = (value) => {
|
||||
form.tags = value.detail.tags;
|
||||
}
|
||||
|
||||
const onSubmit = async () => {
|
||||
const response = await postCreate(form);
|
||||
navigate(`/post/${response.id}`);
|
||||
};
|
||||
</script>
|
||||
|
||||
<section class="hero is-primary">
|
||||
<div class="hero-body">
|
||||
<p class="title">Upload</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<form on:submit|preventDefault={onSubmit}>
|
||||
<div class="field">
|
||||
<label for="file" class="label">Image File</label>
|
||||
<div class="control">
|
||||
<div class="file">
|
||||
<label class="file-label">
|
||||
<input
|
||||
id="file"
|
||||
class="file-input"
|
||||
type="file"
|
||||
name="resume"
|
||||
on:change={onFileChange}
|
||||
/>
|
||||
<span class="file-cta">
|
||||
<span class="file-icon" />
|
||||
<span class="file-label"> Choose a file… </span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{#if currentProgress > 0 && currentProgress < 100}
|
||||
<p class="help">{currentProgress}%</p>
|
||||
{/if}
|
||||
{#if fileName !== ""}
|
||||
<p class="help">{fileName} uploaded</p>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="source" class="label">Source URL</label>
|
||||
<div class="control">
|
||||
<input
|
||||
id="source"
|
||||
class="input"
|
||||
type="url"
|
||||
placeholder="Source URL"
|
||||
bind:value={form.source_url}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="tags" class="label">Tags</label>
|
||||
<div class="control" id="tags">
|
||||
<Tags on:tags={onTagChange} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button type="submit" class="button is-primary">Submit</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
1275
web/app/yarn.lock
1275
web/app/yarn.lock
File diff suppressed because it is too large
Load Diff
5622
web/static/bundle.js
5622
web/static/bundle.js
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@ -5,8 +5,8 @@
|
||||
<title>{{ .title }}</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.2/css/bulma.min.css">
|
||||
<script defer src="/static/bundle.js"></script>
|
||||
<link rel="stylesheet" href="/static/bundle.css">
|
||||
<script>
|
||||
window.BASE_URL = {{ .base_url }}
|
||||
</script>
|
||||
|
Loading…
Reference in New Issue
Block a user