chore: more work on the app
This commit is contained in:
parent
32771c7422
commit
ce866725ff
@ -5,6 +5,13 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type UserType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
User_Normal UserType = iota + 1
|
||||||
|
User_Admin
|
||||||
|
)
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
Id string
|
Id string
|
||||||
Username string
|
Username string
|
||||||
|
@ -471,11 +471,19 @@ func (x Handle) createContext(handler *Handle, mode AnswerType, r *http.Request)
|
|||||||
Prefix: r.URL.Path,
|
Prefix: r.URL.Path,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if mode != JSON {
|
||||||
for _, r := range r.Cookies() {
|
for _, r := range r.Cookies() {
|
||||||
if r.Name == "auth" {
|
if r.Name == "auth" {
|
||||||
token = &r.Value
|
token = &r.Value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
t := r.Header.Get("token")
|
||||||
|
if t != "" {
|
||||||
|
token = &t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO check that the token is still valid
|
// TODO check that the token is still valid
|
||||||
|
|
||||||
@ -512,6 +520,10 @@ func Redirect(path string, mode AnswerType, w http.ResponseWriter, r *http.Reque
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Logoff(mode AnswerType, w http.ResponseWriter, r *http.Request) {
|
func Logoff(mode AnswerType, w http.ResponseWriter, r *http.Request) {
|
||||||
|
if (mode == JSON) {
|
||||||
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
|
w.Write([]byte("\"Not Authorized\""))
|
||||||
|
} else {
|
||||||
// Delete cookie
|
// Delete cookie
|
||||||
cookie := &http.Cookie{
|
cookie := &http.Cookie{
|
||||||
Name: "auth",
|
Name: "auth",
|
||||||
@ -520,6 +532,7 @@ func Logoff(mode AnswerType, w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
http.SetCookie(w, cookie)
|
http.SetCookie(w, cookie)
|
||||||
Redirect("/login", mode, w, r)
|
Redirect("/login", mode, w, r)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func notAuth(mode AnswerType, w http.ResponseWriter, r *http.Request) {
|
func notAuth(mode AnswerType, w http.ResponseWriter, r *http.Request) {
|
||||||
|
129
users.go
129
users.go
@ -11,6 +11,7 @@ import (
|
|||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
|
||||||
dbtypes "git.andr3h3nriqu3s.com/andr3/fyp/logic/db_types"
|
dbtypes "git.andr3h3nriqu3s.com/andr3/fyp/logic/db_types"
|
||||||
|
"git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
|
||||||
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
|
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -169,12 +170,12 @@ func usersEndpints(db *sql.DB, handle *Handle) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(dat.Username) == 0 || len(dat.Password) == 0 || len(dat.Email) == 0 {
|
if len(dat.Username) == 0 || len(dat.Password) == 0 || len(dat.Email) == 0 {
|
||||||
return c.SendJSONStatus(w, http.StatusBadRequest, "Please provide a valid json");
|
return c.SendJSONStatus(w, http.StatusBadRequest, "Please provide a valid json")
|
||||||
}
|
}
|
||||||
|
|
||||||
rows, err := db.Query("select username, email from users where username=$1 or email=$2;", dat.Username, dat.Email)
|
rows, err := db.Query("select username, email from users where username=$1 or email=$2;", dat.Username, dat.Email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.Error500(err);
|
return c.Error500(err)
|
||||||
}
|
}
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
@ -185,10 +186,10 @@ func usersEndpints(db *sql.DB, handle *Handle) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return c.Error500(err)
|
return c.Error500(err)
|
||||||
}
|
}
|
||||||
if (db_email == dat.Email) {
|
if db_email == dat.Email {
|
||||||
return c.SendJSONStatus(w, http.StatusBadRequest, "Email already in use!")
|
return c.SendJSONStatus(w, http.StatusBadRequest, "Email already in use!")
|
||||||
}
|
}
|
||||||
if (db_username == dat.Username) {
|
if db_username == dat.Username {
|
||||||
return c.SendJSONStatus(w, http.StatusBadRequest, "Username already in use!")
|
return c.SendJSONStatus(w, http.StatusBadRequest, "Username already in use!")
|
||||||
}
|
}
|
||||||
panic("Unrechable")
|
panic("Unrechable")
|
||||||
@ -335,6 +336,87 @@ func usersEndpints(db *sql.DB, handle *Handle) {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Handles updating users
|
||||||
|
handle.Post("/user/info", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
|
||||||
|
if !CheckAuthLevel(int(dbtypes.User_Normal), w, r, c) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if c.Mode != JSON {
|
||||||
|
return c.Error500(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserData struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var dat UserData
|
||||||
|
|
||||||
|
if err := c.ToJSON(r, &dat); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if dat.Id != c.User.Id && c.User.UserType != int(dbtypes.User_Admin) {
|
||||||
|
return c.SendJSONStatus(w, 401, "You need to be an admin to update another users account")
|
||||||
|
}
|
||||||
|
|
||||||
|
if dat.Id != c.User.Id {
|
||||||
|
var data struct {
|
||||||
|
Id string
|
||||||
|
}
|
||||||
|
|
||||||
|
err := utils.GetDBOnce(c, &data, "users where id=$1", dat.Id)
|
||||||
|
if err == NotFoundError {
|
||||||
|
return c.JsonBadRequest(w, "User does not exist")
|
||||||
|
} else if err != nil {
|
||||||
|
return c.Error500(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var data struct {
|
||||||
|
Id string
|
||||||
|
}
|
||||||
|
|
||||||
|
err := utils.GetDBOnce(c, &data, "users where email=$1", dat.Email)
|
||||||
|
if err != nil && err != NotFoundError {
|
||||||
|
return c.Error500(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != NotFoundError {
|
||||||
|
if data.Id == dat.Id {
|
||||||
|
return c.JsonBadRequest(w, "Email is the name as the previous one!")
|
||||||
|
} else {
|
||||||
|
return c.JsonBadRequest(w, "Email already in use")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = c.Db.Exec("update users set email=$2 where id=$1", dat.Id, dat.Email)
|
||||||
|
if err != nil {
|
||||||
|
return c.Error500(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var user struct {
|
||||||
|
Id string
|
||||||
|
Username string
|
||||||
|
Email string
|
||||||
|
User_Type int
|
||||||
|
}
|
||||||
|
|
||||||
|
err = utils.GetDBOnce(c, &user, "users where id=$1", dat.Id)
|
||||||
|
if err != nil {
|
||||||
|
return c.Error500(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
toReturnUser := dbtypes.User{
|
||||||
|
Id: user.Id,
|
||||||
|
Username: user.Username,
|
||||||
|
Email: user.Email,
|
||||||
|
UserType: user.User_Type,
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.SendJSON(w, toReturnUser)
|
||||||
|
})
|
||||||
|
|
||||||
handle.Post("/user/info/email", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
|
handle.Post("/user/info/email", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
|
||||||
if !CheckAuthLevel(1, w, r, c) {
|
if !CheckAuthLevel(1, w, r, c) {
|
||||||
return nil
|
return nil
|
||||||
@ -367,7 +449,44 @@ func usersEndpints(db *sql.DB, handle *Handle) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if c.Mode == JSON {
|
if c.Mode == JSON {
|
||||||
return c.Error500(nil)
|
|
||||||
|
var dat struct {
|
||||||
|
Old_Password string `json:"old_password"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
Password2 string `json:"password2"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.ToJSON(r, &dat); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if dat.Password == "" {
|
||||||
|
return c.JsonBadRequest(w, "Password can not be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if dat.Password != dat.Password2 {
|
||||||
|
return c.JsonBadRequest(w, "New passwords did not match")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Logger.Warn("test", "dat", dat)
|
||||||
|
|
||||||
|
_, login := generateToken(db, c.User.Email, dat.Old_Password)
|
||||||
|
if !login {
|
||||||
|
return c.JsonBadRequest(w, "Password is incorrect");
|
||||||
|
}
|
||||||
|
|
||||||
|
salt := generateSalt()
|
||||||
|
hash_password, err := hashPassword(dat.Password, salt)
|
||||||
|
if err != nil {
|
||||||
|
return c.Error500(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = db.Exec("update users set salt=$1, password=$2 where id=$3", salt, hash_password, c.User.Id)
|
||||||
|
if err != nil {
|
||||||
|
return c.Error500(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.SendJSON(w, c.User.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
r.ParseForm()
|
r.ParseForm()
|
||||||
|
43
webpage/src/lib/FileUpload.svelte
Normal file
43
webpage/src/lib/FileUpload.svelte
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
let { replace_slot, accept, file } = $props<{
|
||||||
|
replace_slot?: boolean,
|
||||||
|
accept?: string,
|
||||||
|
file?: File,
|
||||||
|
}>();
|
||||||
|
|
||||||
|
let fileInput: HTMLInputElement;
|
||||||
|
|
||||||
|
let fileData: string | undefined = $state(undefined);
|
||||||
|
|
||||||
|
function onChange(e: Event & {currentTarget: HTMLInputElement}) {
|
||||||
|
if (!e.currentTarget.files) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
file = e.currentTarget.files[0];
|
||||||
|
|
||||||
|
const fileReader = new FileReader();
|
||||||
|
fileReader.onloadend = () => {
|
||||||
|
fileData = String(fileReader.result);
|
||||||
|
//elm.classList.add("adapt");
|
||||||
|
}
|
||||||
|
fileReader.readAsDataURL(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="icon-holder">
|
||||||
|
<button class="icon" class:adapt={replace_slot && file} on:click={() => fileInput.click()}>
|
||||||
|
{#if replace_slot && file}
|
||||||
|
<slot name="replaced" file={file}>
|
||||||
|
<img src={fileData} alt="" />
|
||||||
|
<slot name="replaced-name">
|
||||||
|
Image Uploaded
|
||||||
|
</slot>
|
||||||
|
</slot>
|
||||||
|
{:else}
|
||||||
|
<slot></slot>
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
|
<input id="file" name="file" type="file" required accept={accept} bind:this={fileInput} on:change={onChange} />
|
||||||
|
</div>
|
52
webpage/src/lib/MessageSimple.svelte
Normal file
52
webpage/src/lib/MessageSimple.svelte
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<script context="module" lang="ts">
|
||||||
|
export type DisplayFn = (
|
||||||
|
msg: string,
|
||||||
|
options?: {
|
||||||
|
type?: 'error' | 'success';
|
||||||
|
timeToShow?: number;
|
||||||
|
}
|
||||||
|
) => void;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
let message = $state<string | undefined>(undefined);
|
||||||
|
let type = $state<'error' | 'success'>('error');
|
||||||
|
|
||||||
|
let timeout: number | undefined = undefined;
|
||||||
|
|
||||||
|
export function display(
|
||||||
|
msg: string,
|
||||||
|
options?: {
|
||||||
|
type?: 'error' | 'success';
|
||||||
|
timeToShow?: number;
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
if (timeout) clearTimeout(timeout);
|
||||||
|
|
||||||
|
if (!msg) {
|
||||||
|
message = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let { type: l_type, timeToShow } = options ?? { type: 'error', timeToShow: undefined };
|
||||||
|
|
||||||
|
if (l_type) {
|
||||||
|
type = l_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
message = msg;
|
||||||
|
|
||||||
|
if (timeToShow) {
|
||||||
|
timeout = setTimeout(() => {
|
||||||
|
message = undefined;
|
||||||
|
timeout = undefined;
|
||||||
|
}, timeToShow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if message}
|
||||||
|
<div class="form-msg {type}">
|
||||||
|
{message}
|
||||||
|
</div>
|
||||||
|
{/if}
|
59
webpage/src/routes/models/+page.svelte
Normal file
59
webpage/src/routes/models/+page.svelte
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
let list = $state<{
|
||||||
|
name: string,
|
||||||
|
id: string,
|
||||||
|
}[]>([]);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>
|
||||||
|
Models
|
||||||
|
</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
{#if list.length > 0}
|
||||||
|
<div class="list-header">
|
||||||
|
<h2>My Models</h2>
|
||||||
|
<div class="expand"></div>
|
||||||
|
<a class="button" href="/models/add">
|
||||||
|
New
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
Name
|
||||||
|
</th>
|
||||||
|
<th>
|
||||||
|
<!-- Open Button -->
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{#each list as item}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
{item.name}
|
||||||
|
</td>
|
||||||
|
<td class="text-center">
|
||||||
|
<a class="button simple" href="/models/edit?id={item.id}">
|
||||||
|
Edit
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{:else}
|
||||||
|
<h2 class="text-center">
|
||||||
|
You don't have any models
|
||||||
|
</h2>
|
||||||
|
<div class="text-center">
|
||||||
|
<a class="button padded" href="/models/add">
|
||||||
|
Create a new model
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</main>
|
80
webpage/src/routes/models/add/+page.svelte
Normal file
80
webpage/src/routes/models/add/+page.svelte
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import FileUpload from "src/lib/FileUpload.svelte";
|
||||||
|
import MessageSimple from "src/lib/MessageSimple.svelte";
|
||||||
|
|
||||||
|
import "src/styles/forms.css";
|
||||||
|
|
||||||
|
let submitted = $state(false);
|
||||||
|
|
||||||
|
let message: MessageSimple;
|
||||||
|
|
||||||
|
let buttonClicked: Promise<void> = $state(Promise.resolve());
|
||||||
|
|
||||||
|
let data = $state<{
|
||||||
|
name: string,
|
||||||
|
file?: File,
|
||||||
|
}>({
|
||||||
|
name: '',
|
||||||
|
file: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
function onSubmit() {
|
||||||
|
message.display("");
|
||||||
|
buttonClicked = new Promise<void>(() => {});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>Create new Model</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<h1>
|
||||||
|
Create new Model
|
||||||
|
</h1>
|
||||||
|
<form class:submitted on:submit|preventDefault={onSubmit}>
|
||||||
|
<fieldset>
|
||||||
|
<label for="name">Name</label>
|
||||||
|
<input id="name" name="name" required bind:value={data.name} />
|
||||||
|
<!--{{if .NameFoundError}}
|
||||||
|
<span class="form-msg error">
|
||||||
|
You already have a model with that name.
|
||||||
|
</span>
|
||||||
|
{{end}}-->
|
||||||
|
</fieldset>
|
||||||
|
<fieldset class="file-upload" >
|
||||||
|
<label for="file">Base image</label>
|
||||||
|
<div class="form-msg">
|
||||||
|
Please provide a base image.<br/>
|
||||||
|
This image is a sample of the images that you are going to classfiy.
|
||||||
|
</div>
|
||||||
|
<FileUpload replace_slot bind:file={data.file} >
|
||||||
|
<img src="/imgs/upload-icon.png" alt="" />
|
||||||
|
<span>
|
||||||
|
Upload image
|
||||||
|
</span>
|
||||||
|
<div slot="replaced-name">
|
||||||
|
<span>
|
||||||
|
Image selected
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</FileUpload>
|
||||||
|
</fieldset>
|
||||||
|
<MessageSimple bind:this={message} />
|
||||||
|
{#await buttonClicked}
|
||||||
|
<div class="text-center">
|
||||||
|
File Uploading
|
||||||
|
</div>
|
||||||
|
{:then}
|
||||||
|
<button>
|
||||||
|
Create
|
||||||
|
</button>
|
||||||
|
{/await}
|
||||||
|
</form>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
main {
|
||||||
|
padding: 20px 15vw;
|
||||||
|
}
|
||||||
|
</style>
|
129
webpage/src/routes/user/info/+page.svelte
Normal file
129
webpage/src/routes/user/info/+page.svelte
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
import { userStore } from 'src/routes/UserStore.svelte';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
|
import 'src/styles/forms.css';
|
||||||
|
import { post } from 'src/lib/requests.svelte';
|
||||||
|
import MessageSimple, { type DisplayFn } from 'src/lib/MessageSimple.svelte';
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
if (!userStore.isLogin()) {
|
||||||
|
goto('/login');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let email = $state(userStore.user?.email ?? '');
|
||||||
|
|
||||||
|
let passwordData = $state({
|
||||||
|
old_password: '',
|
||||||
|
password: '',
|
||||||
|
password2: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
let submiitedEmail = $state(false);
|
||||||
|
let submiitedPassword = $state(false);
|
||||||
|
|
||||||
|
let msgEmail: MessageSimple;
|
||||||
|
let msgPassword: MessageSimple;
|
||||||
|
|
||||||
|
async function onSubmitEmail() {
|
||||||
|
submiitedEmail = true;
|
||||||
|
msgEmail.display('');
|
||||||
|
|
||||||
|
if (!userStore.user) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
let req = await post('user/info', {
|
||||||
|
id: userStore.user.id,
|
||||||
|
email: email
|
||||||
|
});
|
||||||
|
userStore.user = {
|
||||||
|
...userStore.user,
|
||||||
|
...req
|
||||||
|
};
|
||||||
|
msgEmail.display('User updated successufly!', { type: 'success', timeToShow: 10000 });
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Response) {
|
||||||
|
msgEmail.display(await e.json());
|
||||||
|
} else {
|
||||||
|
msgEmail.display('Could not update email');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onSubmitPassword() {
|
||||||
|
submiitedPassword = true;
|
||||||
|
msgPassword.display('');
|
||||||
|
if (!userStore.user) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await post('user/info/password', passwordData);
|
||||||
|
passwordData = { old_password: '', password: '', password2: '' };
|
||||||
|
msgPassword.display('Password updated successufly!', { type: 'success', timeToShow: 10000 });
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof Response) {
|
||||||
|
msgPassword.display(await e.json());
|
||||||
|
} else {
|
||||||
|
msgPassword.display('Could not update password');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>User Info</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<div class="login-page">
|
||||||
|
<div>
|
||||||
|
<h1>User Infomation</h1>
|
||||||
|
<form on:submit|preventDefault={onSubmitEmail} class:submiitedEmail>
|
||||||
|
<fieldset>
|
||||||
|
<label for="email">Email</label>
|
||||||
|
<input type="email" required name="email" bind:value={email} />
|
||||||
|
</fieldset>
|
||||||
|
<MessageSimple bind:this={msgEmail} />
|
||||||
|
<button> Update </button>
|
||||||
|
</form>
|
||||||
|
<form on:submit|preventDefault={onSubmitPassword} class:submiitedPassword>
|
||||||
|
<fieldset>
|
||||||
|
<label for="old_password">Old Password</label>
|
||||||
|
<input
|
||||||
|
required
|
||||||
|
bind:value={passwordData.old_password}
|
||||||
|
name="old_password"
|
||||||
|
type="password"
|
||||||
|
/>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<label for="password">New Password</label>
|
||||||
|
<input required bind:value={passwordData.password} name="password" type="password" />
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<label for="password2">Repeat New Password</label>
|
||||||
|
<input required bind:value={passwordData.password2} name="password2" type="password" />
|
||||||
|
</fieldset>
|
||||||
|
<MessageSimple bind:this={msgPassword} />
|
||||||
|
<div>
|
||||||
|
<button> Update </button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<!-- TODO Delete -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
/* Login Page */
|
||||||
|
.login-page {
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
& > div {
|
||||||
|
width: 40vw;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -37,6 +37,10 @@ button {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.button {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
.flex {
|
.flex {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
BIN
webpage/static/imgs/upload-icon.png
Normal file
BIN
webpage/static/imgs/upload-icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.4 KiB |
Loading…
Reference in New Issue
Block a user