Compare commits
10 commits
905cc40fa1
...
3be0755074
| Author | SHA1 | Date | |
|---|---|---|---|
| 3be0755074 | |||
| 929f22abeb | |||
| 5b48c5a8c5 | |||
| 47ed7af853 | |||
| 9c98afc27c | |||
| 1f68142640 | |||
| 044c21840c | |||
| 49b0a962a5 | |||
| 86de4e5827 | |||
| cfc985ffb0 |
28 changed files with 4394 additions and 2418 deletions
6
.forgejo/workflows/demo.yaml
Normal file
6
.forgejo/workflows/demo.yaml
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
on: [push]
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: docker
|
||||||
|
steps:
|
||||||
|
- run: echo All Good
|
||||||
6217
package-lock.json
generated
6217
package-lock.json
generated
File diff suppressed because it is too large
Load diff
44
package.json
44
package.json
|
|
@ -15,40 +15,44 @@
|
||||||
"format": "prettier --write src/"
|
"format": "prettier --write src/"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@pinia/colada": "^0.13.0",
|
"@pinia/colada": "^0.17.6",
|
||||||
"@primevue/forms": "^4.2.2",
|
"@primevue/forms": "^4.2.2",
|
||||||
"@primevue/themes": "^4.2.2",
|
"@primevue/themes": "^4.4.1",
|
||||||
"pinia": "^2.2.6",
|
"@tailwindcss/postcss": "^4.1.15",
|
||||||
|
"pinia": "^3.0.3",
|
||||||
"primeicons": "^7.0.0",
|
"primeicons": "^7.0.0",
|
||||||
"primevue": "^4.2.2",
|
"primevue": "^4.4.1",
|
||||||
"tailwindcss-primeui": "^0.3.4",
|
"tailwindcss-primeui": "^0.6.1",
|
||||||
"vue": "^3.5.12",
|
"uqr": "^0.1.2",
|
||||||
|
"vue": "^3.5.22",
|
||||||
"vue-router": "^4.4.5"
|
"vue-router": "^4.4.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@tailwindcss/vite": "^4.1.15",
|
||||||
"@tsconfig/node22": "^22.0.0",
|
"@tsconfig/node22": "^22.0.0",
|
||||||
"@types/jsdom": "^21.1.7",
|
"@types/jsdom": "^27.0.0",
|
||||||
"@types/node": "^22.9.0",
|
"@types/node": "^24.9.0",
|
||||||
"@vitejs/plugin-vue": "^5.1.4",
|
"@vitejs/plugin-vue": "^6.0.1",
|
||||||
"@vitest/eslint-plugin": "1.1.7",
|
"@vitest/eslint-plugin": "1.3.23",
|
||||||
"@vue/eslint-config-prettier": "^10.1.0",
|
"@vue/eslint-config-prettier": "^10.1.0",
|
||||||
"@vue/eslint-config-typescript": "^14.1.3",
|
"@vue/eslint-config-typescript": "^14.1.3",
|
||||||
|
"@vue/language-server": "^3.1.1",
|
||||||
"@vue/test-utils": "^2.4.6",
|
"@vue/test-utils": "^2.4.6",
|
||||||
"@vue/tsconfig": "^0.5.1",
|
"@vue/tsconfig": "^0.8.1",
|
||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
"eslint": "^9.14.0",
|
"eslint": "^9.14.0",
|
||||||
"eslint-plugin-vue": "^9.30.0",
|
"eslint-plugin-vue": "^10.5.1",
|
||||||
"jsdom": "^25.0.1",
|
"jsdom": "^27.0.1",
|
||||||
"json-server": "^1.0.0-beta.3",
|
"json-server": "^1.0.0-beta.3",
|
||||||
"npm-run-all2": "^7.0.1",
|
"npm-run-all2": "^8.0.4",
|
||||||
"postcss": "^8.4.49",
|
"postcss": "^8.4.49",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
"sass-embedded": "^1.81.0",
|
"sass-embedded": "^1.81.0",
|
||||||
"tailwindcss": "^3.4.15",
|
"tailwindcss": "^4.1.15",
|
||||||
"typescript": "~5.6.3",
|
"typescript": "~5.9.3",
|
||||||
"vite": "^5.4.10",
|
"vite": "^7.1.11",
|
||||||
"vite-plugin-vue-devtools": "^7.5.4",
|
"vite-plugin-vue-devtools": "^8.0.3",
|
||||||
"vitest": "^2.1.4",
|
"vitest": "^3.2.4",
|
||||||
"vue-tsc": "^2.1.10"
|
"vue-tsc": "^3.1.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
export default {
|
export default {
|
||||||
plugins: {
|
plugins: {
|
||||||
tailwindcss: {},
|
'@tailwindcss/postcss': {},
|
||||||
autoprefixer: {},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
80
src/App.vue
80
src/App.vue
|
|
@ -1,78 +1,20 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from "vue";
|
import { computed } from 'vue'
|
||||||
import { RouterView } from 'vue-router'
|
import { RouterView, useRoute } from 'vue-router'
|
||||||
|
|
||||||
import Footer from "@/components/Footer.vue";
|
import Default from '@/layouts/Default.vue'
|
||||||
import Sidebar from "@/components/Sidebar.vue";
|
|
||||||
import TopBar from "@/components/TopBar.vue";
|
|
||||||
import { useSessionStore } from "@/stores/sessionStore";
|
|
||||||
|
|
||||||
const sessionStore = useSessionStore();
|
const route = useRoute();
|
||||||
|
|
||||||
const sidebarSize = computed(() => {
|
const layout = computed(() => {
|
||||||
return sessionStore.isAnonymous ? '0' : '90px';
|
const layoutComponent = route.meta.layout || Default;
|
||||||
|
return layoutComponent;
|
||||||
});
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="act-layout">
|
<component :is="layout">
|
||||||
<TopBar class="act-header"></TopBar>
|
<router-view name="layout" />
|
||||||
<div class="act-sidebar" v-if="!sessionStore.isAnonymous">
|
</component>
|
||||||
<Sidebar></Sidebar>
|
|
||||||
</div>
|
|
||||||
<div class="act-content">
|
|
||||||
<main class="">
|
|
||||||
<RouterView />
|
|
||||||
</main>
|
|
||||||
<div class="act-footer">
|
|
||||||
<Footer></Footer>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
$header-height: 60px !default;
|
|
||||||
$sidebar-size: 90px !default;
|
|
||||||
|
|
||||||
.act-layout {
|
|
||||||
// To scroll only in the middle, see https://jsfiddle.net/VNVqs/
|
|
||||||
margin-bottom: v-bind(sidebarSize); // for scrolling
|
|
||||||
margin-top: $header-height; // for scrolling
|
|
||||||
.act-footer {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.act-sidebar {
|
|
||||||
bottom: 0;
|
|
||||||
height: v-bind(sidebarSize);
|
|
||||||
left: 0;
|
|
||||||
position: fixed;
|
|
||||||
right: 0;
|
|
||||||
z-index: 20;
|
|
||||||
}
|
|
||||||
.act-header {
|
|
||||||
height: $header-height;
|
|
||||||
left: 0;
|
|
||||||
position: fixed;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
z-index: 20;
|
|
||||||
}
|
|
||||||
@media (screen(sm)) {
|
|
||||||
margin-bottom: 0;
|
|
||||||
margin-left: v-bind(sidebarSize);
|
|
||||||
.act-footer {
|
|
||||||
display: block
|
|
||||||
}
|
|
||||||
.act-sidebar {
|
|
||||||
left: 0;
|
|
||||||
height: 100%;
|
|
||||||
position: fixed;
|
|
||||||
width: v-bind(sidebarSize);
|
|
||||||
}
|
|
||||||
.act-header {
|
|
||||||
left: v-bind(sidebarSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
@tailwind base;
|
@import "tailwindcss";
|
||||||
@tailwind components;
|
@import "tailwindcss/preflight";
|
||||||
@tailwind utilities;
|
@import "tailwindcss-primeui";
|
||||||
|
@import 'primeicons/primeicons.css';
|
||||||
|
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
.nav-item {
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 16px;
|
|
||||||
@apply hover:bg-surface-800 rounded-border text-surface-300 hover:text-white;
|
|
||||||
span {
|
|
||||||
@apply text-base;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.nav-icon {
|
|
||||||
@apply text-base;
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
<!--Only displays for > sm screens -->
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -8,13 +10,14 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@import "@/assets/nav-items.css";
|
@reference "tailwindcss";
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
background-color: #E0E0E0;
|
background-color: #E0E0E0;
|
||||||
color: #777;
|
color: #777;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
@media (screen(sm)) {
|
@media (min-width: 640px) {
|
||||||
aside {
|
aside {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,14 @@
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<nav>
|
<nav>
|
||||||
<RouterLink
|
<RouterLink class="nav-item" to="/">
|
||||||
class="nav-item"
|
|
||||||
to="/">
|
|
||||||
<i class="pi pi-home nav-icon" />
|
<i class="pi pi-home nav-icon" />
|
||||||
<span>Home</span>
|
<span>Home</span>
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
<RouterLink class="nav-item" to="/sticker">
|
<a class="nav-item">
|
||||||
<i class="pi pi-image nav-icon" />
|
<i class="pi pi-image nav-icon" />
|
||||||
<span>Sticker</span>
|
<span>Sticker</span>
|
||||||
</RouterLink>
|
</a>
|
||||||
<a class="nav-item">
|
<a class="nav-item">
|
||||||
<i class="pi pi-users nav-icon" />
|
<i class="pi pi-users nav-icon" />
|
||||||
<span>Team</span>
|
<span>Team</span>
|
||||||
|
|
@ -21,9 +19,7 @@
|
||||||
<i class="pi pi-calendar nav-icon" />
|
<i class="pi pi-calendar nav-icon" />
|
||||||
<span>Events</span>
|
<span>Events</span>
|
||||||
</a>
|
</a>
|
||||||
<RouterLink
|
<RouterLink class="nav-item" to="/profile">
|
||||||
class="nav-item"
|
|
||||||
to="/profile">
|
|
||||||
<i class="pi pi-user nav-icon" />
|
<i class="pi pi-user nav-icon" />
|
||||||
<span>Profile</span>
|
<span>Profile</span>
|
||||||
</RouterLink>
|
</RouterLink>
|
||||||
|
|
@ -31,12 +27,37 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@import "@/assets/nav-items.css";
|
@reference "tailwindcss";
|
||||||
|
@reference "tailwindcss-primeui";
|
||||||
|
|
||||||
nav {
|
nav {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
}
|
}
|
||||||
@media (screen(sm)) {
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Duplicated in Sidebar.vue. */
|
||||||
|
.nav-item {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
@apply hover:bg-surface-800 rounded-border text-surface-300 hover:text-white;
|
||||||
|
span {
|
||||||
|
@apply text-base;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.nav-icon {
|
||||||
|
@apply text-base;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@media (min-width: 640px) {
|
||||||
nav {
|
nav {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,22 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import QRCodeImage from './QRCodeImage.vue';
|
import { defineProps } from 'vue';
|
||||||
|
|
||||||
|
import { renderSVG } from 'uqr';
|
||||||
|
|
||||||
|
const { content } = defineProps({
|
||||||
|
content: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const svg = renderSVG(content);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="qr-code">
|
<div class="qr-code">
|
||||||
<QRCodeImage class="qr-code-image" />
|
|
||||||
<!-- <span class="uuid">1337-9339</span>-->
|
<!-- <span class="uuid">1337-9339</span>-->
|
||||||
|
<div :innerHTML="svg"></div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -13,6 +24,7 @@ import QRCodeImage from './QRCodeImage.vue';
|
||||||
.qr-code {
|
.qr-code {
|
||||||
aspect-ratio: 1 / 1;
|
aspect-ratio: 1 / 1;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
|
padding: 3px;
|
||||||
}
|
}
|
||||||
.uuid {
|
.uuid {
|
||||||
color: rgba(51, 51, 51, 0.91);
|
color: rgba(51, 51, 51, 0.91);
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ const toggle = (event: Event) => { menu.value.toggle(event); };
|
||||||
<aside>
|
<aside>
|
||||||
<Navigation></Navigation>
|
<Navigation></Navigation>
|
||||||
<div class="mt-auto act-avatar">
|
<div class="mt-auto act-avatar">
|
||||||
|
<!-- TODO -->
|
||||||
<a class="nav-item" @click="toggle">
|
<a class="nav-item" @click="toggle">
|
||||||
<div v-if="sessionStore.isAnonymous">
|
<div v-if="sessionStore.isAnonymous">
|
||||||
<Avatar icon="pi pi-user" size="large" shape="circle" />
|
<Avatar icon="pi pi-user" size="large" shape="circle" />
|
||||||
|
|
@ -67,7 +68,9 @@ const toggle = (event: Event) => { menu.value.toggle(event); };
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@import "@/assets/nav-items.css";
|
@reference "tailwindcss";
|
||||||
|
@reference "tailwindcss-primeui";
|
||||||
|
|
||||||
aside {
|
aside {
|
||||||
background-color: #333;
|
background-color: #333;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -77,7 +80,20 @@ aside {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@media (screen(sm)) {
|
/* Duplicated in Navigation.vue. */
|
||||||
|
.nav-item {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
@apply hover:bg-surface-800 rounded-border text-surface-300 hover:text-white;
|
||||||
|
span {
|
||||||
|
@apply text-base;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (min-width: 640px) {
|
||||||
aside {
|
aside {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
.act-avatar {
|
.act-avatar {
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,13 @@ import Image from 'primevue/image'
|
||||||
import { type Organization, useOrganization } from '@/queries/organization'
|
import { type Organization, useOrganization } from '@/queries/organization'
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
id,
|
||||||
layout, color,
|
layout, color,
|
||||||
orgs,
|
orgs,
|
||||||
message1, message2,
|
message1, message2,
|
||||||
message3, message4,
|
message3, message4,
|
||||||
} = defineProps([
|
} = defineProps([
|
||||||
|
'id',
|
||||||
'layout', 'color',
|
'layout', 'color',
|
||||||
"orgs",
|
"orgs",
|
||||||
'message1', 'message2',
|
'message1', 'message2',
|
||||||
|
|
@ -39,7 +41,6 @@ const color2 = computed(() => {
|
||||||
const { state: allOrgs } = useOrganization();
|
const { state: allOrgs } = useOrganization();
|
||||||
|
|
||||||
const myOrgs = computed(() => {
|
const myOrgs = computed(() => {
|
||||||
console.log(orgs)
|
|
||||||
if (allOrgs.value.status !== 'success') {
|
if (allOrgs.value.status !== 'success') {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
@ -47,10 +48,14 @@ const myOrgs = computed(() => {
|
||||||
return orgs.includes(org.id);
|
return orgs.includes(org.id);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const qrContent = computed(() => {
|
||||||
|
return `https://anactivist.com/${id}`;
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="sticker">
|
<div class="sticker">
|
||||||
<QRCode class="qr-code"></QRCode>
|
<QRCode class="qr-code" :content="qrContent"></QRCode>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="orgs">
|
<div class="orgs">
|
||||||
<div v-for="org in myOrgs" :key="org.id">
|
<div v-for="org in myOrgs" :key="org.id">
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,7 @@ header {
|
||||||
.act-menubar-end {
|
.act-menubar-end {
|
||||||
margin-left: 0 !important;
|
margin-left: 0 !important;
|
||||||
}
|
}
|
||||||
@media (screen(sm)) {
|
@media (min-width: 640px) {
|
||||||
.act-useraction {
|
.act-useraction {
|
||||||
background-color: red;
|
background-color: red;
|
||||||
display: none; // sidebar will have them
|
display: none; // sidebar will have them
|
||||||
|
|
|
||||||
77
src/layouts/Default.vue
Normal file
77
src/layouts/Default.vue
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from "vue";
|
||||||
|
import { RouterView } from 'vue-router';
|
||||||
|
|
||||||
|
import Footer from "@/components/Footer.vue";
|
||||||
|
import Sidebar from "@/components/Sidebar.vue";
|
||||||
|
import TopBar from "@/components/TopBar.vue";
|
||||||
|
|
||||||
|
import { useSessionStore } from "@/stores/sessionStore";
|
||||||
|
|
||||||
|
const sessionStore = useSessionStore();
|
||||||
|
|
||||||
|
const sidebarSize = computed(() => {
|
||||||
|
return sessionStore.isAnonymous ? '0' : '90px';
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="act-layout">
|
||||||
|
<TopBar class="act-header"></TopBar>
|
||||||
|
<div class="act-sidebar" v-if="!sessionStore.isAnonymous">
|
||||||
|
<Sidebar></Sidebar>
|
||||||
|
</div>
|
||||||
|
<div class="act-content">
|
||||||
|
<main class="">
|
||||||
|
<router-view />
|
||||||
|
</main>
|
||||||
|
<div class="act-footer">
|
||||||
|
<Footer></Footer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
$header-height: 60px !default;
|
||||||
|
$sidebar-size: 90px !default;
|
||||||
|
|
||||||
|
.act-layout {
|
||||||
|
// To scroll only in the middle, see https://jsfiddle.net/VNVqs/
|
||||||
|
margin-bottom: v-bind(sidebarSize); // for scrolling
|
||||||
|
margin-top: $header-height; // for scrolling
|
||||||
|
.act-footer {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.act-sidebar {
|
||||||
|
bottom: 0;
|
||||||
|
height: v-bind(sidebarSize);
|
||||||
|
left: 0;
|
||||||
|
position: fixed;
|
||||||
|
right: 0;
|
||||||
|
z-index: 20;
|
||||||
|
}
|
||||||
|
.act-header {
|
||||||
|
height: $header-height;
|
||||||
|
left: 0;
|
||||||
|
position: fixed;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
z-index: 20;
|
||||||
|
}
|
||||||
|
@media (min-width: 640px) {
|
||||||
|
margin-bottom: 0;
|
||||||
|
margin-left: v-bind(sidebarSize);
|
||||||
|
.act-footer {
|
||||||
|
display: block
|
||||||
|
}
|
||||||
|
.act-sidebar {
|
||||||
|
left: 0;
|
||||||
|
height: 100%;
|
||||||
|
position: fixed;
|
||||||
|
width: v-bind(sidebarSize);
|
||||||
|
}
|
||||||
|
.act-header {
|
||||||
|
left: v-bind(sidebarSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
9
src/layouts/Neat.vue
Normal file
9
src/layouts/Neat.vue
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { RouterView } from 'vue-router'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<main class="">
|
||||||
|
<router-view />
|
||||||
|
</main>
|
||||||
|
</template>
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
import { createRouter, createWebHistory } from 'vue-router'
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
|
|
||||||
import { useSessionStore } from "@/stores/sessionStore";
|
import { useSessionStore } from "@/stores/sessionStore";
|
||||||
|
import Neat from '../layouts/Neat.vue'
|
||||||
|
|
||||||
import AboutView from '../views/AboutView.vue'
|
import AboutView from '../views/AboutView.vue'
|
||||||
import HomeView from '../views/HomeView.vue'
|
import HomeView from '../views/HomeView.vue'
|
||||||
import LoginView from '../views/LoginView.vue'
|
import LoginView from '../views/LoginView.vue'
|
||||||
|
import PrintView from '../views/PrintView.vue'
|
||||||
import ProfileView from '../views/ProfileView.vue'
|
import ProfileView from '../views/ProfileView.vue'
|
||||||
import StickerBuilderView from '../views/StickerBuilderView.vue'
|
import StickerBuilderView from '../views/StickerBuilderView.vue'
|
||||||
import StickerView from '../views/StickerView.vue'
|
import StickerView from '../views/StickerView.vue'
|
||||||
|
|
@ -31,13 +34,22 @@ const router = createRouter({
|
||||||
name: 'home',
|
name: 'home',
|
||||||
path: '/',
|
path: '/',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
component: PrintView,
|
||||||
|
meta: {
|
||||||
|
layout: Neat,
|
||||||
|
title: 'Print',
|
||||||
|
},
|
||||||
|
name: 'print',
|
||||||
|
path: '/print/:user_id',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
component: ProfileView,
|
component: ProfileView,
|
||||||
meta: {
|
meta: {
|
||||||
title: 'Profile',
|
title: 'Profile',
|
||||||
},
|
},
|
||||||
name: 'profile',
|
name: 'profile',
|
||||||
path: '/profile/:username?',
|
path: '/profile/:user_id?',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
component: StickerBuilderView,
|
component: StickerBuilderView,
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,16 @@
|
||||||
import {acceptHMRUpdate, defineStore} from 'pinia'
|
import {acceptHMRUpdate, defineStore} from 'pinia'
|
||||||
import { useUserStore } from '@/stores/userStore'
|
import { useUserStore } from '@/stores/userStore'
|
||||||
|
|
||||||
|
|
||||||
export interface User {
|
export interface User {
|
||||||
name: string,
|
name: string,
|
||||||
sticker_ids: string[],
|
sticker_ids: string[],
|
||||||
username: string,
|
user_id: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
const anonymous: User = {
|
const anonymous: User = {
|
||||||
name: 'Anonymous',
|
name: 'Anonymous',
|
||||||
sticker_ids: [],
|
sticker_ids: [],
|
||||||
username: 'anonymous',
|
user_id: 'anonymous',
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface State {
|
export interface State {
|
||||||
|
|
@ -21,22 +20,26 @@ export interface State {
|
||||||
export const useSessionStore = defineStore('session', {
|
export const useSessionStore = defineStore('session', {
|
||||||
actions: {
|
actions: {
|
||||||
async login(username: string, password: string) {
|
async login(username: string, password: string) {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('username', username);
|
||||||
|
formData.append('password', password);
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`/api/session`,
|
`/api-2/sessions`,
|
||||||
{
|
{
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify({username, password}),
|
body: formData,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const json = await response.json();
|
const json = await response.json();
|
||||||
console.log('session created', json);
|
localStorage.setItem('access_token', json.access_token);
|
||||||
|
const payload = parseJwt(json.access_token);
|
||||||
// Fetch the user record.
|
// Fetch the user record.
|
||||||
const userStore= useUserStore();
|
const userStore= useUserStore();
|
||||||
let user = await userStore.fetch(json.username);
|
let user = await userStore.fetch(payload.user_id);
|
||||||
console.log('user', user);
|
|
||||||
if (user) {
|
if (user) {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
}
|
}
|
||||||
|
console.log('session started');
|
||||||
},
|
},
|
||||||
logout() {
|
logout() {
|
||||||
console.log('logout');
|
console.log('logout');
|
||||||
|
|
@ -53,7 +56,7 @@ export const useSessionStore = defineStore('session', {
|
||||||
return state.user.name[0] || '';
|
return state.user.name[0] || '';
|
||||||
},
|
},
|
||||||
isAnonymous: (state: State): boolean => {
|
isAnonymous: (state: State): boolean => {
|
||||||
return state.user.username === 'anonymous';
|
return state.user.user_id === 'anonymous';
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
state: (): State => {
|
state: (): State => {
|
||||||
|
|
@ -63,6 +66,20 @@ export const useSessionStore = defineStore('session', {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
function parseJwt(token: string) {
|
||||||
|
var base64Url = token.split('.')[1];
|
||||||
|
if (!base64Url) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
|
||||||
|
var jsonPayload = decodeURIComponent(window.atob(base64).split('').map(function(c) {
|
||||||
|
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
|
||||||
|
}).join(''));
|
||||||
|
|
||||||
|
return JSON.parse(jsonPayload);
|
||||||
|
}
|
||||||
|
|
||||||
if (import.meta.hot) {
|
if (import.meta.hot) {
|
||||||
import.meta.hot.accept(acceptHMRUpdate(useSessionStore, import.meta.hot))
|
import.meta.hot.accept(acceptHMRUpdate(useSessionStore, import.meta.hot))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,14 +7,15 @@ import { useUserStore } from '@/stores/userStore'
|
||||||
export interface Sticker {
|
export interface Sticker {
|
||||||
color: string,
|
color: string,
|
||||||
createdBy: string | null,
|
createdBy: string | null,
|
||||||
|
id: string,
|
||||||
layout: string,
|
layout: string,
|
||||||
orgs: string[],
|
|
||||||
message1: string | null,
|
message1: string | null,
|
||||||
message2: string | null,
|
message2: string | null,
|
||||||
message3: string | null,
|
message3: string | null,
|
||||||
message4: string | null,
|
message4: string | null,
|
||||||
owner: string | null,
|
owner: string | null,
|
||||||
id: string,
|
orgs: string[],
|
||||||
|
url: string | null,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const activist: Sticker = {
|
export const activist: Sticker = {
|
||||||
|
|
@ -46,8 +47,8 @@ export interface State {
|
||||||
stickers: Sticker[];
|
stickers: Sticker[];
|
||||||
}
|
}
|
||||||
|
|
||||||
function randomColor() {
|
function randomColor(): string {
|
||||||
return colors[Math.floor(Math.random() * colors.length)];
|
return colors[Math.floor(Math.random() * colors.length)] as string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useStickersStore = defineStore('stickers', {
|
export const useStickersStore = defineStore('stickers', {
|
||||||
|
|
@ -65,8 +66,14 @@ export const useStickersStore = defineStore('stickers', {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// TODO: Check if we have it already and if it's stale.
|
// TODO: Check if we have it already and if it's stale.
|
||||||
|
const accessToken = localStorage.getItem('access_token');
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`/api/sticker/${id}`,
|
`/api-2/sticker/${id}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${accessToken}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
);
|
);
|
||||||
const json = await response.json();
|
const json = await response.json();
|
||||||
this.stickers.push(json);
|
this.stickers.push(json);
|
||||||
|
|
@ -77,9 +84,12 @@ export const useStickersStore = defineStore('stickers', {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`/api/sticker/${id}`,
|
`/api-2/sticker/${id}`,
|
||||||
{
|
{
|
||||||
body: JSON.stringify(sticker),
|
body: JSON.stringify(sticker),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
@ -88,7 +98,7 @@ export const useStickersStore = defineStore('stickers', {
|
||||||
// this.sticker = json;
|
// this.sticker = json;
|
||||||
},
|
},
|
||||||
setOwner(sticker: Sticker, user: User) {
|
setOwner(sticker: Sticker, user: User) {
|
||||||
sticker.owner = user.username;
|
sticker.owner = user.user_id;
|
||||||
this.put(sticker.id);
|
this.put(sticker.id);
|
||||||
const userStore= useUserStore();
|
const userStore= useUserStore();
|
||||||
userStore.addSticker(user.username, sticker.id);
|
userStore.addSticker(user.username, sticker.id);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
export interface User {
|
export interface User {
|
||||||
name: string,
|
name: string,
|
||||||
sticker_ids: string[],
|
sticker_ids: string[],
|
||||||
username: string,
|
user_id: string,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@ export interface State {
|
||||||
|
|
||||||
export const useUserStore = defineStore('user', {
|
export const useUserStore = defineStore('user', {
|
||||||
actions: {
|
actions: {
|
||||||
addSticker(username: string, id: string) {
|
addSticker(user_id: string, id: string) {
|
||||||
let user = this.getUser(username);
|
let user = this.getUser(user_id);
|
||||||
if (user) {
|
if (user) {
|
||||||
user.sticker_ids.push(id);
|
user.sticker_ids.push(id);
|
||||||
}
|
}
|
||||||
|
|
@ -19,20 +19,28 @@ export const useUserStore = defineStore('user', {
|
||||||
addUser(user: User) {
|
addUser(user: User) {
|
||||||
this.users.push(user);
|
this.users.push(user);
|
||||||
},
|
},
|
||||||
async fetch(username: string) {
|
async fetch(user_id: string) {
|
||||||
let user = this.getUser(username);
|
let user = this.getUser(user_id);
|
||||||
if (!user) {
|
if (!user) {
|
||||||
// TODO: Check if we have it already and if it's stale.
|
// TODO: Check if we have it already and if it's stale.
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`/api/user/${username}`,
|
`/api-2/user/${user_id}`,
|
||||||
);
|
);
|
||||||
user = await response.json();
|
if (!response.ok) {
|
||||||
if (user) {
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
this.users.push(user);
|
}
|
||||||
|
const user_data = await response.json();
|
||||||
|
if (user_data !== undefined) {
|
||||||
|
user = {
|
||||||
|
user_id: user_data.id,
|
||||||
|
name: user_data.display_name,
|
||||||
|
sticker_ids: [],
|
||||||
|
};
|
||||||
|
this.addUser(user);
|
||||||
|
|
||||||
// prepop sticker store with user stickers
|
// pre-pop sticker store with user stickers
|
||||||
const stickersStore = useStickersStore();
|
const stickersStore = useStickersStore();
|
||||||
user.sticker_ids.forEach((sticker_id: string) => {
|
user.sticker_ids?.forEach((sticker_id: string) => {
|
||||||
stickersStore.fetch(sticker_id);
|
stickersStore.fetch(sticker_id);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -42,8 +50,8 @@ export const useUserStore = defineStore('user', {
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
getUser: (state: State) => {
|
getUser: (state: State) => {
|
||||||
return (username: string): User | undefined => {
|
return (user_id: string): User | undefined => {
|
||||||
return state.users.find((user: User) => user.username === username)
|
return state.users.find((user: User) => user.user_id === user_id)
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -8,15 +8,15 @@ import Button from "primevue/button";
|
||||||
<Panel header="user1 contributed to project1">
|
<Panel header="user1 contributed to project1">
|
||||||
<Button
|
<Button
|
||||||
as="router-link"
|
as="router-link"
|
||||||
to="/profile/joe">
|
to="/profile/b356ab50-4657-4b60-aba9-71a2baab3bfe">
|
||||||
Joe's Profile
|
Joe's Profile
|
||||||
</Button>
|
</Button>
|
||||||
</Panel>
|
</Panel>
|
||||||
<Panel header="user2 contributed to project2">
|
<Panel header="user2 contributed to project2">
|
||||||
<Button
|
<Button
|
||||||
as="router-link"
|
as="router-link"
|
||||||
to="/profile/jen">
|
to="/profile/2f0ad329-4f24-4d22-8827-d1a5d3237a4d">
|
||||||
Jen's Profile
|
Kim's Profile
|
||||||
</Button>
|
</Button>
|
||||||
</Panel>
|
</Panel>
|
||||||
<Panel header="user3 contributed to project3">
|
<Panel header="user3 contributed to project3">
|
||||||
|
|
|
||||||
|
|
@ -19,14 +19,18 @@ const route = useRoute();
|
||||||
const next = route.query.next;
|
const next = route.query.next;
|
||||||
|
|
||||||
|
|
||||||
function handleLogin({ valid }) {
|
function handleLogin({ valid } : { valid: boolean }) {
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
console.log('Input is not valid.')
|
console.log('Input is not valid.')
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sessionStore.login(username.value, password.value).then(() => {
|
sessionStore.login(username.value, password.value).then(() => {
|
||||||
|
if (typeof route.query.next !== 'string') {
|
||||||
|
console.error('next is not valid string.')
|
||||||
|
return;
|
||||||
|
}
|
||||||
console.log(`go to ${next}`)
|
console.log(`go to ${next}`)
|
||||||
router.push(next);
|
router.push(route.query.next);
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
});
|
});
|
||||||
|
|
@ -36,8 +40,17 @@ const initialValues = reactive({
|
||||||
username: 'joe',
|
username: 'joe',
|
||||||
password: 'password',
|
password: 'password',
|
||||||
});
|
});
|
||||||
const resolver = ({ values }) => {
|
interface MessageType {
|
||||||
const errors = {};
|
message: string;
|
||||||
|
}
|
||||||
|
// TODO: Get the proper type. Maybe Record<string, any> in primevue/
|
||||||
|
// forms/forms.
|
||||||
|
interface FormErrors {
|
||||||
|
username?: MessageType[];
|
||||||
|
password?: MessageType[];
|
||||||
|
}
|
||||||
|
const resolver = ({ values }: { values: Record<string, any> }) => {
|
||||||
|
const errors: FormErrors = {};
|
||||||
if (!values.username) {
|
if (!values.username) {
|
||||||
errors.username = [{message: 'Username is required.'}];
|
errors.username = [{message: 'Username is required.'}];
|
||||||
}
|
}
|
||||||
|
|
@ -46,9 +59,8 @@ const resolver = ({ values }) => {
|
||||||
errors.password = [{message: 'Password is required.'}];
|
errors.password = [{message: 'Password is required.'}];
|
||||||
}
|
}
|
||||||
password.value = values.password;
|
password.value = values.password;
|
||||||
return { errors }
|
return { values, errors }
|
||||||
};
|
};
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
||||||
68
src/views/PrintView.vue
Normal file
68
src/views/PrintView.vue
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, ref } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
|
||||||
|
import Avatar from 'primevue/avatar'
|
||||||
|
import Panel from 'primevue/panel'
|
||||||
|
|
||||||
|
import Sticker from '@/components/Sticker.vue'
|
||||||
|
import { type User } from '@/stores/sessionStore'
|
||||||
|
import { useUserStore } from '@/stores/userStore'
|
||||||
|
import { useStickersStore } from '@/stores/stickersStore'
|
||||||
|
|
||||||
|
|
||||||
|
const userStore = useUserStore();
|
||||||
|
const { getUser } = userStore;
|
||||||
|
const route = useRoute();
|
||||||
|
const username = ref<string>(route.params.username as string);
|
||||||
|
console.log(username);
|
||||||
|
let user = ref<User>();
|
||||||
|
const stickersStore = useStickersStore();
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await userStore.fetch(username.value);
|
||||||
|
user.value = getUser(username.value);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<!-- <Panel>-->
|
||||||
|
<!-- from {{ user?.name }}-->
|
||||||
|
<!-- </Panel>-->
|
||||||
|
<Panel>
|
||||||
|
<div class="stickers">
|
||||||
|
<div class="stickerWrapper" v-for="sticker_id in user?.sticker_ids">
|
||||||
|
<RouterLink :to="{ name: 'sticker', params: { id: sticker_id }}">
|
||||||
|
<Sticker v-bind="stickersStore.getSticker(sticker_id)" />
|
||||||
|
</RouterLink>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="stickers">
|
||||||
|
<div v-for="sticker in stickersStore.getStickersCreatedBy(user?.username)">
|
||||||
|
<div class="stickerWrapper">
|
||||||
|
<Sticker v-bind="sticker" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Panel>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.stickers {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
.stickerWrapper {
|
||||||
|
background-color: #F2F2F2;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 10px;
|
||||||
|
.sticker {
|
||||||
|
width: 300px;
|
||||||
|
@media (min-width: 640px) {
|
||||||
|
zoom: 250%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -21,17 +21,17 @@ const sessionStore = useSessionStore();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const { getUser } = userStore;
|
const { getUser } = userStore;
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const username = ref<string>(route.params.username as string);
|
const user_id = ref<string>(route.params.user_id as string);
|
||||||
let user = ref<User>();
|
let user = ref<User>();
|
||||||
const stickersStore = useStickersStore();
|
const stickersStore = useStickersStore();
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await userStore.fetch(username.value);
|
await userStore.fetch(user_id.value);
|
||||||
user.value = getUser(username.value);
|
user.value = getUser(user_id.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
const myPage = computed(() => {
|
const myPage = computed(() => {
|
||||||
return !sessionStore.isAnonymous && sessionStore.user.username == user.value?.username;
|
return !sessionStore.isAnonymous && sessionStore.user.user_id == user.value?.user_id;
|
||||||
});
|
});
|
||||||
|
|
||||||
const meterValue = ref([
|
const meterValue = ref([
|
||||||
|
|
@ -88,8 +88,13 @@ const events = [{
|
||||||
v-if="myPage">
|
v-if="myPage">
|
||||||
New sticker
|
New sticker
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button
|
||||||
|
as="router-link"
|
||||||
|
:to="{ name: 'print', params: { user_id: user?.user_id }}">
|
||||||
|
Print
|
||||||
|
</Button>
|
||||||
<div class="stickers">
|
<div class="stickers">
|
||||||
<div v-for="sticker in stickersStore.getStickersCreatedBy(user?.username)">
|
<div v-for="sticker in stickersStore.getStickersCreatedBy(user?.user_id)">
|
||||||
<div class="stickerWrapper">
|
<div class="stickerWrapper">
|
||||||
<RouterLink :to="{ name: 'sticker', params: { id: sticker.id }}">
|
<RouterLink :to="{ name: 'sticker', params: { id: sticker.id }}">
|
||||||
<Sticker v-bind="sticker" />
|
<Sticker v-bind="sticker" />
|
||||||
|
|
@ -115,7 +120,7 @@ const events = [{
|
||||||
:pt="{ eventOpposite: { class: 'hidden' } }"
|
:pt="{ eventOpposite: { class: 'hidden' } }"
|
||||||
:value="events">
|
:value="events">
|
||||||
<template #marker="slotProps">
|
<template #marker="slotProps">
|
||||||
<span class="flex w-8 h-8 items-center justify-center text-white rounded-full z-10 shadow-sm" :style="{ backgroundColor: slotProps.item.color }">
|
<span class="flex w-8 h-8 items-center justify-center text-white rounded-full z-10 shadow-xs" :style="{ backgroundColor: slotProps.item.color }">
|
||||||
<i :class="slotProps.item.icon"></i>
|
<i :class="slotProps.item.icon"></i>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -155,6 +160,8 @@ const events = [{
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
@reference "tailwindcss";
|
||||||
|
|
||||||
.profilePage {
|
.profilePage {
|
||||||
@apply p-2 gap-2;
|
@apply p-2 gap-2;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -172,7 +179,7 @@ const events = [{
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
.sticker {
|
.sticker {
|
||||||
width: 300px;
|
width: 300px;
|
||||||
@media (screen(sm)) {
|
@media (min-width: 640px) {
|
||||||
zoom: 250%;
|
zoom: 250%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ const orgOptions = computed(() => {
|
||||||
<div id="StickerPage">
|
<div id="StickerPage">
|
||||||
<!-- <Button class="w-20" type="submit" severity="secondary" label="Take a snapshot" />-->
|
<!-- <Button class="w-20" type="submit" severity="secondary" label="Take a snapshot" />-->
|
||||||
<div id="StickerWrapper" class="sticky z-10">
|
<div id="StickerWrapper" class="sticky z-10">
|
||||||
<Sticker v-bind="sticker" id="Sticker1" />
|
<Sticker v-bind="sticker" class="medium" />
|
||||||
<!-- <p>8-inch sticker</p>-->
|
<!-- <p>8-inch sticker</p>-->
|
||||||
<!-- <Sticker id="Sticker2" v-bind="sticker2" />-->
|
<!-- <Sticker id="Sticker2" v-bind="sticker2" />-->
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -206,6 +206,9 @@ const orgOptions = computed(() => {
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
@reference "tailwindcss";
|
||||||
|
@reference "tailwindcss-primeui";
|
||||||
|
|
||||||
label {
|
label {
|
||||||
@apply font-medium text-surface-900 dark:text-surface-0 mb-1 block;
|
@apply font-medium text-surface-900 dark:text-surface-0 mb-1 block;
|
||||||
}
|
}
|
||||||
|
|
@ -226,9 +229,9 @@ label {
|
||||||
gap: 1em;
|
gap: 1em;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
top: 60px;
|
top: 60px;
|
||||||
#Sticker1 {
|
.medium {
|
||||||
width: 300px;
|
width: 300px;
|
||||||
@media (screen(sm)) {
|
@media (min-width: 640px) {
|
||||||
zoom: 250%;
|
zoom: 250%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,10 @@ const second = computed(() => {
|
||||||
})
|
})
|
||||||
|
|
||||||
function claimSticker() {
|
function claimSticker() {
|
||||||
|
if (!sticker.value) {
|
||||||
|
console.error("No sticker to set owner");
|
||||||
|
return;
|
||||||
|
}
|
||||||
stickersStore.setOwner(sticker.value, sessionStore.user as User);
|
stickersStore.setOwner(sticker.value, sessionStore.user as User);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -93,7 +97,7 @@ function claimSticker() {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
#Sticker {
|
#Sticker {
|
||||||
width: 300px;
|
width: 300px;
|
||||||
@media (screen(sm)) {
|
@media (min-width: 640px) {
|
||||||
zoom: 250%;
|
zoom: 250%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
import primeui from 'tailwindcss-primeui'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
content: [
|
content: [
|
||||||
"./index.html",
|
"./index.html",
|
||||||
"./src/**/*.{vue,js,ts,jsx,tsx}",
|
"./src/**/*.{vue,js,ts,jsx,tsx}",
|
||||||
|
"./node_modules/**/*.{vue,js,ts,jsx,tsx}",
|
||||||
],
|
],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
|
|
@ -13,5 +16,5 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [require('tailwindcss-primeui')],
|
plugins: [primeui],
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,20 @@
|
||||||
import { fileURLToPath, URL } from 'node:url'
|
import { fileURLToPath, URL } from 'node:url'
|
||||||
|
|
||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
|
import tailwindcss from '@tailwindcss/vite';
|
||||||
import vue from '@vitejs/plugin-vue'
|
import vue from '@vitejs/plugin-vue'
|
||||||
import vueDevTools from 'vite-plugin-vue-devtools'
|
// import vueDevTools from 'vite-plugin-vue-devtools'
|
||||||
|
|
||||||
// https://vite.dev/config/
|
// https://vite.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
vue(),
|
vue(),
|
||||||
// vueDevTools(),
|
// vueDevTools(),
|
||||||
|
tailwindcss(),
|
||||||
],
|
],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
|
|
@ -22,6 +24,12 @@ export default defineConfig({
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
secure: false,
|
secure: false,
|
||||||
rewrite: (path) => path.replace(/^\/api/, ''),
|
rewrite: (path) => path.replace(/^\/api/, ''),
|
||||||
|
},
|
||||||
|
'^/api-2/': {
|
||||||
|
target: 'http://127.0.0.1:5000',
|
||||||
|
changeOrigin: true,
|
||||||
|
secure: false,
|
||||||
|
rewrite: (path) => path.replace(/^\/api-2/, ''),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue