From e89c261f566f05ab4347f60dfd68b01303ad93ec Mon Sep 17 00:00:00 2001 From: jedi <git@m.j3d1.de> Date: Wed, 10 May 2023 23:28:55 +0200 Subject: [PATCH] stash --- frontend/README.md | 29 ++++ frontend/src/App.vue | 12 +- frontend/src/dns.js | 2 + frontend/src/store.js | 9 ++ frontend/src/views/Dashboard.vue | 11 +- frontend/src/views/Inventory.vue | 27 +++- frontend/src/views/Profile.vue | 257 +++++++++++++++++++++++++++++++ frontend/src/views/Settings.vue | 209 +++++++++++++++++++++++++ frontend/vite.config.js | 1 + 9 files changed, 550 insertions(+), 7 deletions(-) create mode 100644 frontend/README.md create mode 100644 frontend/src/views/Profile.vue create mode 100644 frontend/src/views/Settings.vue diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 0000000..8d6c5ef --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,29 @@ +# frontend + +This template should help get you started developing with Vue 3 in Vite. + +## Recommended IDE Setup + +[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin). + +## Customize configuration + +See [Vite Configuration Reference](https://vitejs.dev/config/). + +## Project Setup + +```sh +npm install +``` + +### Compile and Hot-Reload for Development + +```sh +npm run dev +``` + +### Compile and Minify for Production + +```sh +npm run build +``` diff --git a/frontend/src/App.vue b/frontend/src/App.vue index a7280b7..54ad06d 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -3,13 +3,23 @@ <template> <router-view></router-view> + <!-- TODO UI für Freunde liste, add, remove --> </template> <script> +import {mapMutations} from 'vuex'; +import store from '@/store'; + export default { - name: 'App' + name: 'App', + methods: { + ...mapMutations(['init']) + }, + beforeCreate () { + store.commit('init') + } } </script> diff --git a/frontend/src/dns.js b/frontend/src/dns.js index 9446ccb..d3f2a8f 100644 --- a/frontend/src/dns.js +++ b/frontend/src/dns.js @@ -35,6 +35,7 @@ class FallBackResolver { const key = domain + ':' + type; if (key in this._cache && this._cache[key].time > Date.now() - 1000 * 60 * 60) { const age_seconds = Math.ceil(Date.now() / 1000 - this._cache[key].time / 1000); + console.log('cache hit', key, this._cache[key].ttl - age_seconds); return [this._cache[key].data]; } const result = await query( @@ -47,6 +48,7 @@ class FallBackResolver { const first = result.answers[0]; this._cache[key] = {time: Date.now(), ...first}; // TODO hadle multiple answers localStorage.setItem('dns-cache', JSON.stringify(this._cache)); + console.log('cache miss', key, first.ttl); return [first.data]; } } diff --git a/frontend/src/store.js b/frontend/src/store.js index 0cadaff..dff2ce7 100644 --- a/frontend/src/store.js +++ b/frontend/src/store.js @@ -98,7 +98,9 @@ export default createStore({ this.commit('setToken', token); if (keypair) { this.commit('setKey', keypair) + } else { } + router.push('/'); } state.cache_loaded = true; } @@ -118,9 +120,16 @@ export default createStore({ } else { return false; } + }, async lookupServer({state}, {username}) { const domain = username.split('@')[1] + if (domain === 'example.eleon') + return ['10.23.42.186:8000']; + if (domain === 'localhost') + return ['127.0.0.1:8000']; + if (domain === 'example.com') + return ['10.23.42.128:8000']; const request = '_toolshed-server._tcp.' + domain + '.' return await state.resolver.query(request, 'SRV').then( (result) => result.map( diff --git a/frontend/src/views/Dashboard.vue b/frontend/src/views/Dashboard.vue index 6c19b16..1f23884 100644 --- a/frontend/src/views/Dashboard.vue +++ b/frontend/src/views/Dashboard.vue @@ -5,7 +5,16 @@ <h1 class="h3 mb-3">Dashboard</h1> <div class="row"> <div class="col-12"> - + <div class="card"> + <div class="card-header"> + <h5 class="card-title mb-0">Empty card</h5> + </div> + <div class="card-body"> + <div class="logo"> + <img src="/src/assets/icons/toolshed-48x48.png" alt="Toolshed logo"> + </div> + </div> + </div> </div> </div> </div> diff --git a/frontend/src/views/Inventory.vue b/frontend/src/views/Inventory.vue index 8687fa6..350ed44 100644 --- a/frontend/src/views/Inventory.vue +++ b/frontend/src/views/Inventory.vue @@ -2,6 +2,7 @@ <BaseLayout> <main class="content"> <div class="container-fluid p-0"> + <h1 class="h3 mb-3">Inventory Own & Friends"</h1> <div class="row"> <div class="col-12 col-xl-6"> <div class="card"> @@ -28,6 +29,7 @@ <td> <router-link :to="`/inventory/${item.id}`">{{ item.name }}</router-link> </td> + <td>{{ item.owner }}</td> <td class="d-none d-md-table-cell"> <span class="badge bg-secondary text-white">{{ item.availability_policy }}</span> </td> @@ -75,7 +77,7 @@ </div> <div class="card"> - <button class="btn" @click="fetchInventoryItems">Refresh</button> + <button class="btn" @click="getInventoryItems">Refresh</button> <router-link to="/inventory/new" class="btn btn-primary">Add</router-link> </div> </div> @@ -102,14 +104,29 @@ export default { ...BIcons }, computed: { - ...mapGetters(["inventory_items", "loaded_items"]), - ...mapState(["user"]), + ...mapGetters(["inventory_items"]), + username() { + return this.$route.params.username + } }, methods: { - ...mapActions(["fetchInventoryItems", "deleteInventoryItem"]), + ...mapActions(["apiFederatedGet", "getFriends", "getFriendServer"]), + ...mapMutations(["setInventoryItems"]), + async getInventoryItems() { + try { + const servers = await this.getFriends().then(friends => friends.map(friend => this.getFriendServer({username: friend}))) + const urls = servers.map(server => server.then(s => `http://${s}/api/inventory_items/`)) + urls.map(url => url.then(u => this.apiFederatedGet(u).then(items => { + this.setInventoryItems({url: u, items}) + }).catch(e => { + }))) // TODO: handle error + } catch (e) { + console.error(e) + } + }, }, async mounted() { - await this.fetchInventoryItems() + await this.getInventoryItems() } } </script> diff --git a/frontend/src/views/Profile.vue b/frontend/src/views/Profile.vue new file mode 100644 index 0000000..50ecade --- /dev/null +++ b/frontend/src/views/Profile.vue @@ -0,0 +1,257 @@ +<template> + <BaseLayout> + <main class="content"> + <div class="container-fluid p-0"> + + <h1 class="h3 mb-3">Profile</h1> + + <div class="row"> + <div class="col-md-4 col-xl-3"> + <div class="card mb-3"> + <div class="card-header"> + <h5 class="card-title mb-0">Profile Details</h5> + </div> + <div class="card-body text-center"> + <!--<img src="/static/assets/img/avatars/avatar.png" + alt="Christina Mason" class="img-fluid rounded-circle mb-2" width="128" + height="128"/>--> + <h5 class="card-title mb-0"> + {{ user.username }} + </h5> + <div class="text-muted mb-2"> + {{ user.email }} + </div> + + <div> + <a class="btn btn-primary btn-sm" href="#">Follow</a> + <a class="btn btn-primary btn-sm" href="#"><span + data-feather="message-square"></span> Message</a> + </div> + </div> + <!--{% if user.bio %}--> + <hr class="my-0"/> + <div class="card-body"> + <h5 class="h6 card-title">Bio</h5> + <div class="text-muted mb-2"> + {{ user.bio }} + </div> + </div> + <!--{% endif %}--> + <hr class="my-0"/> + <div class="card-body"> + <h5 class="h6 card-title">Skills</h5> + <a href="#" class="badge bg-primary mr-1 my-1">HTML</a> + <a href="#" class="badge bg-primary mr-1 my-1">JavaScript</a> + <a href="#" class="badge bg-primary mr-1 my-1">Sass</a> + <a href="#" class="badge bg-primary mr-1 my-1">Angular</a> + <a href="#" class="badge bg-primary mr-1 my-1">Vue</a> + <a href="#" class="badge bg-primary mr-1 my-1">React</a> + <a href="#" class="badge bg-primary mr-1 my-1">Redux</a> + <a href="#" class="badge bg-primary mr-1 my-1">UI</a> + <a href="#" class="badge bg-primary mr-1 my-1">UX</a> + </div> + <hr class="my-0"/> + <div class="card-body"> + <h5 class="h6 card-title">About</h5> + <ul class="list-unstyled mb-0"> + <!--{% if user.location %}--> + <li class="mb-1"><span data-feather="home" class="feather-sm mr-1"></span> Lives + in <a href="#">{{ user.location }}</a></li> + <!--{% endif %}--> + + <li class="mb-1"><span data-feather="briefcase" class="feather-sm mr-1"></span> + Works at <a href="#">GitHub</a></li> + <li class="mb-1"><span data-feather="map-pin" class="feather-sm mr-1"></span> + From <a href="#">Boston</a></li> + </ul> + </div> + <hr class="my-0"/> + <div class="card-body"> + <h5 class="h6 card-title">Elsewhere</h5> + <ul class="list-unstyled mb-0"> + <li class="mb-1"><span class="fas fa-globe fa-fw mr-1"></span> <a href="#">staciehall.co</a> + </li> + <li class="mb-1"><span class="fab fa-twitter fa-fw mr-1"></span> <a + href="#">Twitter</a> + </li> + <li class="mb-1"><span class="fab fa-facebook fa-fw mr-1"></span> <a href="#">Facebook</a> + </li> + <li class="mb-1"><span class="fab fa-instagram fa-fw mr-1"></span> <a href="#">Instagram</a> + </li> + <li class="mb-1"><span class="fab fa-linkedin fa-fw mr-1"></span> <a href="#">LinkedIn</a> + </li> + </ul> + </div> + </div> + </div> + + <div class="col-md-8 col-xl-9"> + <div class="card"> + <div class="card-header"> + + <h5 class="card-title mb-0">Activities</h5> + </div> + <div class="card-body h-100"> + + <div class="d-flex align-items-start"> + <!--<img src="/static/assets/img/avatars/avatar-5.png" width="36" height="36" + class="rounded-circle mr-2" alt="Vanessa Tucker">--> + <div class="flex-grow-1"> + <small class="float-right text-navy">5m ago</small> + <strong>Vanessa Tucker</strong> started following <strong>Christina + Mason</strong><br/> + <small class="text-muted">Today 7:51 pm</small><br/> + + </div> + </div> + + <hr/> + <div class="d-flex align-items-start"> + <!--<img src="/static/assets/img/avatars/avatar.png" width="36" height="36" + class="rounded-circle mr-2" alt="Charles Hall">--> + <div class="flex-grow-1"> + <small class="float-right text-navy">30m ago</small> + <strong>Charles Hall</strong> posted something on <strong>Christina + Mason</strong>'s timeline<br/> + <small class="text-muted">Today 7:21 pm</small> + + <div class="border text-sm text-muted p-2 mt-1"> + Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem + quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam + nunc, blandit vel, luctus + pulvinar, hendrerit id, lorem. Maecenas nec odio et ante tincidunt + tempus. Donec vitae sapien ut libero venenatis faucibus. Nullam quis + ante. + </div> + + <a href="#" class="btn btn-sm btn-danger mt-1"><i class="feather-sm" + data-feather="heart"></i> + Like</a> + </div> + </div> + + <hr/> + <div class="d-flex align-items-start"> + <!--<img src="/static/assets/img/avatars/avatar-4.png" width="36" height="36" + class="rounded-circle mr-2" alt="Christina Mason">--> + <div class="flex-grow-1"> + <small class="float-right text-navy">1h ago</small> + <strong>Christina Mason</strong> posted a new blog<br/> + + <small class="text-muted">Today 6:35 pm</small> + </div> + </div> + + <hr/> + <div class="d-flex align-items-start"> + <!--<img src="/static/assets/img/avatars/avatar-2.png" width="36" height="36" + class="rounded-circle mr-2" alt="William Harris">--> + <div class="flex-grow-1"> + <small class="float-right text-navy">3h ago</small> + <strong>William Harris</strong> posted two photos on <strong>Christina + Mason</strong>'s timeline<br/> + <small class="text-muted">Today 5:12 pm</small> + + <div class="row g-0 mt-1"> + <div class="col-6 col-md-4 col-lg-4 col-xl-3"> + <!--<img src="/static/assets/img/photos/unsplash-1.jpg" + class="img-fluid pr-2" alt="Unsplash">--> + </div> + <div class="col-6 col-md-4 col-lg-4 col-xl-3"> + <!--<img src="/static/assets/img/photos/unsplash-2.jpg" + class="img-fluid pr-2" alt="Unsplash">--> + </div> + </div> + + <a href="#" class="btn btn-sm btn-danger mt-1"><i class="feather-sm" + data-feather="heart"></i> + Like</a> + </div> + </div> + + <hr/> + <div class="d-flex align-items-start"> + <!--<img src="/static/assets/img/avatars/avatar-2.png" width="36" height="36" + class="rounded-circle mr-2" alt="William Harris">--> + <div class="flex-grow-1"> + <small class="float-right text-navy">1d ago</small> + <strong>William Harris</strong> started following <strong>Christina + Mason</strong><br/> + <small class="text-muted">Yesterday 3:12 pm</small> + + <div class="d-flex align-items-start mt-1"> + <a class="pr-3" href="#"> + <!--<img src="/static/assets/img/avatars/avatar-4.png" width="36" + height="36" class="rounded-circle mr-2" alt="Christina Mason">--> + </a> + <div class="flex-grow-1"> + <div class="border text-sm text-muted p-2 mt-1"> + Nam quam nunc, blandit vel, luctus pulvinar, hendrerit id, + lorem. Maecenas nec odio et ante tincidunt tempus. + </div> + </div> + </div> + </div> + </div> + + <hr/> + <div class="d-flex align-items-start"> + <!--<img src="/static/assets/img/avatars/avatar-4.png" width="36" height="36" + class="rounded-circle mr-2" alt="Christina Mason">--> + <div class="flex-grow-1"> + <small class="float-right text-navy">1d ago</small> + <strong>Christina Mason</strong> posted a new blog<br/> + <small class="text-muted">Yesterday 2:43 pm</small> + </div> + </div> + + <hr/> + <div class="d-flex align-items-start"> + <!--<img src="/static/assets/img/avatars/avatar.png" width="36" height="36" + class="rounded-circle mr-2" alt="Charles Hall">--> + <div class="flex-grow-1"> + <small class="float-right text-navy">1d ago</small> + <strong>Charles Hall</strong> started following <strong>Christina + Mason</strong><br/> + <small class="text-muted">Yesterdag 1:51 pm</small> + </div> + </div> + + <hr/> + <a href="#" class="btn btn-primary btn-block">Load more</a> + </div> + </div> + </div> + </div> + + </div> + </main> + </BaseLayout> +</template> + +<script> +import BaseLayout from "@/components/BaseLayout.vue"; + +export default { + name: 'Profile', + data() { + return { + user: { + name: 'John Doe', + avatar: '/static/assets/img/avatars/avatar.png', + cover: '/static/assets/img/photos/unsplash-1.jpg', + occupation: 'Frontend Developer', + company: 'Facebook Inc.', + email: 'foo@bar.com', + phone: '+12 345 678 001', + address: 'Boulevard of Broken Dreams, 1234', + } + } + }, + components: {BaseLayout}, +} +</script> + +<style scoped> + +</style> \ No newline at end of file diff --git a/frontend/src/views/Settings.vue b/frontend/src/views/Settings.vue new file mode 100644 index 0000000..d143c7e --- /dev/null +++ b/frontend/src/views/Settings.vue @@ -0,0 +1,209 @@ +<template> + <BaseLayout> + <main class="content"> + <div class="container-fluid p-0"> + + <h1 class="h3 mb-3">Settings</h1> + + <div class="row"> + <div class="col-md-3 col-xl-2"> + + <div class="card"> + <div class="card-header"> + <h5 class="card-title mb-0">Profile Settings</h5> + </div> + + <div class="list-group list-group-flush" role="tablist"> + <a class="list-group-item list-group-item-action active" data-toggle="list" + href="#account" role="tab"> + Account + </a> + <a class="list-group-item list-group-item-action" data-toggle="list" + href="#password" role="tab"> + Password + </a> + <a class="list-group-item list-group-item-action" data-toggle="list" href="#" + role="tab"> + Privacy and safety + </a> + <a class="list-group-item list-group-item-action" data-toggle="list" href="#" + role="tab"> + Email notifications + </a> + <a class="list-group-item list-group-item-action" data-toggle="list" href="#" + role="tab"> + Web notifications + </a> + <a class="list-group-item list-group-item-action" data-toggle="list" href="#" + role="tab"> + Widgets + </a> + <a class="list-group-item list-group-item-action" data-toggle="list" href="#" + role="tab"> + Your data + </a> + <a class="list-group-item list-group-item-action" data-toggle="list" href="#" + role="tab"> + Delete account + </a> + </div> + </div> + </div> + + <div class="col-md-9 col-xl-10"> + <div class="tab-content"> + <div class="tab-pane fade show active" id="account" role="tabpanel"> + + <div class="card"> + <div class="card-header"> + + <h5 class="card-title mb-0">Public info</h5> + </div> + <div class="card-body"> + <form> + <div class="row"> + <div class="col-md-8"> + <div class="mb-3"> + <label class="form-label" + for="inputUsername">Username</label> + <input type="text" class="form-control" id="inputUsername" + placeholder="Username"> + </div> + <div class="mb-3"> + <label class="form-label" + for="inputUsername">Biography</label> + <textarea rows="2" class="form-control" id="inputBio" + placeholder="Tell something about yourself"></textarea> + </div> + </div> + <div class="col-md-4"> + <div class="text-center"> + <img alt="Charles Hall" + src="/static/assets/img/avatars/avatar.png" + class="rounded-circle img-responsive mt-2" width="128" + height="128"/> + <div class="mt-2"> + <span class="btn btn-primary"><i + class="fas fa-upload"></i> Upload</span> + </div> + <small>For best results, use an image at least 128px by + 128px in .jpg format</small> + </div> + </div> + </div> + + <button type="submit" class="btn btn-primary">Save changes</button> + </form> + + </div> + </div> + + <div class="card"> + <div class="card-header"> + + <h5 class="card-title mb-0">Private info</h5> + </div> + <div class="card-body"> + <form> + <div class="row"> + <div class="mb-3 col-md-6"> + <label class="form-label" for="inputFirstName">First + name</label> + <input type="text" class="form-control" id="inputFirstName" + placeholder="First name"> + </div> + <div class="mb-3 col-md-6"> + <label class="form-label" for="inputLastName">Last name</label> + <input type="text" class="form-control" id="inputLastName" + placeholder="Last name"> + </div> + </div> + <div class="mb-3"> + <label class="form-label" for="inputEmail4">Email</label> + <input type="email" class="form-control" id="inputEmail4" + placeholder="Email"> + </div> + <div class="mb-3"> + <label class="form-label" for="inputAddress">Address</label> + <input type="text" class="form-control" id="inputAddress" + placeholder="1234 Main St"> + </div> + <div class="mb-3"> + <label class="form-label" for="inputAddress2">Address 2</label> + <input type="text" class="form-control" id="inputAddress2" + placeholder="Apartment, studio, or floor"> + </div> + <div class="row"> + <div class="mb-3 col-md-6"> + <label class="form-label" for="inputCity">City</label> + <input type="text" class="form-control" id="inputCity"> + </div> + <div class="mb-3 col-md-4"> + <label class="form-label" for="inputState">State</label> + <select id="inputState" class="form-control"> + <option selected>Choose...</option> + <option>...</option> + </select> + </div> + <div class="mb-3 col-md-2"> + <label class="form-label" for="inputZip">Zip</label> + <input type="text" class="form-control" id="inputZip"> + </div> + </div> + <button type="submit" class="btn btn-primary">Save changes</button> + </form> + + </div> + </div> + + </div> + <div class="tab-pane fade" id="password" role="tabpanel"> + <div class="card"> + <div class="card-body"> + <h5 class="card-title">Password</h5> + + <form> + <div class="mb-3"> + <label class="form-label" for="inputPasswordCurrent">Current + password</label> + <input type="password" class="form-control" + id="inputPasswordCurrent"> + <small><a href="#">Forgot your password?</a></small> + </div> + <div class="mb-3"> + <label class="form-label" for="inputPasswordNew">New + password</label> + <input type="password" class="form-control" id="inputPasswordNew"> + </div> + <div class="mb-3"> + <label class="form-label" for="inputPasswordNew2">Verify + password</label> + <input type="password" class="form-control" id="inputPasswordNew2"> + </div> + <button type="submit" class="btn btn-primary">Save changes</button> + </form> + + </div> + </div> + </div> + </div> + </div> + </div> + + </div> + </main> + </BaseLayout> +</template> + +<script> +import BaseLayout from "@/components/BaseLayout.vue"; + +export default { + name: 'Settings', + components: {BaseLayout}, +} +</script> + +<style scoped> + +</style> \ No newline at end of file diff --git a/frontend/vite.config.js b/frontend/vite.config.js index c199937..ed6b2f1 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -3,6 +3,7 @@ import {fileURLToPath, URL} from 'node:url' import {defineConfig} from 'vite' import vue from '@vitejs/plugin-vue' +// https://vitejs.dev/config/ export default defineConfig({ plugins: [vue()], resolve: {