Use Pinia Colada for organizations.
This commit is contained in:
parent
f5195e8a48
commit
6b07b56d87
5 changed files with 132 additions and 3 deletions
9
package-lock.json
generated
9
package-lock.json
generated
|
|
@ -8,6 +8,7 @@
|
||||||
"name": "designer",
|
"name": "designer",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@pinia/colada": "^0.13.0",
|
||||||
"@primevue/forms": "^4.2.2",
|
"@primevue/forms": "^4.2.2",
|
||||||
"@primevue/themes": "^4.2.2",
|
"@primevue/themes": "^4.2.2",
|
||||||
"pinia": "^2.2.6",
|
"pinia": "^2.2.6",
|
||||||
|
|
@ -1217,6 +1218,14 @@
|
||||||
"integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==",
|
"integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@pinia/colada": {
|
||||||
|
"version": "0.13.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@pinia/colada/-/colada-0.13.0.tgz",
|
||||||
|
"integrity": "sha512-Ah6uv1spRCu3I0QmimOMwrkKMcQK1+Ov2Qroz4PFD2/i9gwRUddj7Pz9kNcg5xXp2WWEPmnvkhTr2GgdqTUaUQ==",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/posva"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@pkgjs/parseargs": {
|
"node_modules/@pkgjs/parseargs": {
|
||||||
"version": "0.11.0",
|
"version": "0.11.0",
|
||||||
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
"format": "prettier --write src/"
|
"format": "prettier --write src/"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@pinia/colada": "^0.13.0",
|
||||||
"@primevue/forms": "^4.2.2",
|
"@primevue/forms": "^4.2.2",
|
||||||
"@primevue/themes": "^4.2.2",
|
"@primevue/themes": "^4.2.2",
|
||||||
"pinia": "^2.2.6",
|
"pinia": "^2.2.6",
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import './assets/main.css'
|
||||||
|
|
||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
import { createPinia } from 'pinia'
|
import { createPinia } from 'pinia'
|
||||||
|
import { PiniaColada } from '@pinia/colada';
|
||||||
|
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import router from './router'
|
import router from './router'
|
||||||
|
|
@ -12,6 +13,7 @@ import StyleClass from 'primevue/styleclass';
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
|
|
||||||
app.use(createPinia())
|
app.use(createPinia())
|
||||||
|
app.use(PiniaColada)
|
||||||
app.use(router)
|
app.use(router)
|
||||||
app.use(PrimeVue, {
|
app.use(PrimeVue, {
|
||||||
theme: {
|
theme: {
|
||||||
|
|
|
||||||
22
src/queries/organization.ts
Normal file
22
src/queries/organization.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { defineQuery, useQuery } from '@pinia/colada'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
export interface Organization {
|
||||||
|
abbrev: string,
|
||||||
|
id: string,
|
||||||
|
logo: string,
|
||||||
|
name: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useOrganization = defineQuery(() => {
|
||||||
|
const search = ref('')
|
||||||
|
const { state, ...rest } = useQuery({
|
||||||
|
key: ['organization'],
|
||||||
|
query: () => fetch('/api/organization').then((res) => res.json()),
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
...rest,
|
||||||
|
state,
|
||||||
|
search,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, toRaw, watch } from 'vue'
|
import { computed, ref, toRaw, watch } from 'vue'
|
||||||
|
|
||||||
import Button from 'primevue/button';
|
import Button from 'primevue/button';
|
||||||
import Card from 'primevue/card';
|
import Card from 'primevue/card';
|
||||||
|
import Chip from 'primevue/chip';
|
||||||
import ColorPicker from 'primevue/colorpicker';
|
import ColorPicker from 'primevue/colorpicker';
|
||||||
import { Form } from '@primevue/forms';
|
import { Form } from '@primevue/forms';
|
||||||
|
import Image from 'primevue/image';
|
||||||
import InputGroup from 'primevue/inputgroup';
|
import InputGroup from 'primevue/inputgroup';
|
||||||
import InputGroupAddon from 'primevue/inputgroupaddon';
|
import InputGroupAddon from 'primevue/inputgroupaddon';
|
||||||
import InputText from 'primevue/inputtext';
|
import InputText from 'primevue/inputtext';
|
||||||
|
|
@ -15,12 +17,16 @@ import IconLayout2Vue from '@/components/layouts/IconLayout2.vue';
|
||||||
import IconLayout3Vue from '@/components/layouts/IconLayout3.vue';
|
import IconLayout3Vue from '@/components/layouts/IconLayout3.vue';
|
||||||
import IconLayout4Vue from '@/components/layouts/IconLayout4.vue';
|
import IconLayout4Vue from '@/components/layouts/IconLayout4.vue';
|
||||||
import Sticker from '@/components/Sticker.vue';
|
import Sticker from '@/components/Sticker.vue';
|
||||||
import { useSticker } from '@/composables/sticker'
|
import { type Organization, useOrganization } from '@/queries/organization';
|
||||||
import { activist, type Sticker as StickerType } from '@/stores/stickersStore'
|
import { useSticker } from '@/composables/sticker';
|
||||||
|
import { activist, type Sticker as StickerType } from '@/stores/stickersStore';
|
||||||
|
|
||||||
|
|
||||||
|
const { state: orgs } = useOrganization();
|
||||||
|
const myOrgs = ref<Organization[]>([]);
|
||||||
const original = useSticker();
|
const original = useSticker();
|
||||||
let sticker = ref<StickerType>(activist);
|
let sticker = ref<StickerType>(activist);
|
||||||
|
const orgFilter = ref("");
|
||||||
|
|
||||||
// When the original sticker appears, copy it into a local coy.
|
// When the original sticker appears, copy it into a local coy.
|
||||||
watch(original, (newValue) => {
|
watch(original, (newValue) => {
|
||||||
|
|
@ -35,6 +41,22 @@ const resolver = () => {
|
||||||
function onFormSubmit() {
|
function onFormSubmit() {
|
||||||
original.value = sticker.value;
|
original.value = sticker.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addOrg(id: string) {
|
||||||
|
myOrgs.value.push(orgs.value.data.find((org: Organization) => org.id === id));
|
||||||
|
}
|
||||||
|
function removeOrg(orgId: string) {
|
||||||
|
const myIds = myOrgs.value.map(org => org.id);
|
||||||
|
let start = myIds.indexOf(orgId);
|
||||||
|
myOrgs.value.splice(start, 1);
|
||||||
|
}
|
||||||
|
const orgOptions = computed(() => {
|
||||||
|
const myIds = myOrgs.value.map(org => org.id);
|
||||||
|
return orgs.value.data.filter(({ id, name }) => {
|
||||||
|
return !myIds.includes(id)
|
||||||
|
&& name.toLowerCase().includes(orgFilter.value.toLowerCase());
|
||||||
|
});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -52,6 +74,44 @@ function onFormSubmit() {
|
||||||
@submit="onFormSubmit">
|
@submit="onFormSubmit">
|
||||||
<div class="bg-surface-50 dark:bg-surface-950 px-3 py-5 md:px-12">
|
<div class="bg-surface-50 dark:bg-surface-950 px-3 py-5 md:px-12">
|
||||||
<div class="grid grid-cols-12 gap-4">
|
<div class="grid grid-cols-12 gap-4">
|
||||||
|
<div class="col-span-12 flex flex-col gap-4">
|
||||||
|
<label for="nickname2">Organizations I Support</label>
|
||||||
|
<div class="my-orgs">
|
||||||
|
<div v-for="org in myOrgs" :key="org.id">
|
||||||
|
<Chip
|
||||||
|
:image="org.logo"
|
||||||
|
:label="org.abbrev"
|
||||||
|
removable
|
||||||
|
v-on:remove="removeOrg(org.id)"
|
||||||
|
>
|
||||||
|
</Chip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<InputText
|
||||||
|
id="nickname2"
|
||||||
|
type="text"
|
||||||
|
class="w-full"
|
||||||
|
placeholder="search"
|
||||||
|
v-model="orgFilter"
|
||||||
|
/>
|
||||||
|
<div v-if="orgs.status === 'pending'">
|
||||||
|
loading
|
||||||
|
</div>
|
||||||
|
<div v-if="orgs.error">
|
||||||
|
There was an error loading organization data.
|
||||||
|
</div>
|
||||||
|
<div class="logos" v-else-if="orgs.data">
|
||||||
|
<div v-for="org in orgOptions" :key="org.id">
|
||||||
|
<a @click="addOrg(org.id)">
|
||||||
|
<Image class="logo" :src="org.logo" alt="Image" />
|
||||||
|
{{ org.name }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="divider" />
|
||||||
|
|
||||||
<div class="col-span-12">
|
<div class="col-span-12">
|
||||||
<label for="nickname2">Layout {{sticker.layout}}</label>
|
<label for="nickname2">Layout {{sticker.layout}}</label>
|
||||||
<div class="flex flex-wrap gap-4">
|
<div class="flex flex-wrap gap-4">
|
||||||
|
|
@ -160,4 +220,39 @@ label {
|
||||||
width: 400px;
|
width: 400px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.logos {
|
||||||
|
display: flex;
|
||||||
|
gap: 1em;
|
||||||
|
height: 200px;
|
||||||
|
overflow: auto;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
> div {
|
||||||
|
background-color: #F8F8F8;
|
||||||
|
border: 1px solid #bbb;
|
||||||
|
border-radius: 4px;
|
||||||
|
height: 60px;
|
||||||
|
padding: 6px;
|
||||||
|
width: 310px;
|
||||||
|
> a {
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
gap: 1em;
|
||||||
|
.logo {
|
||||||
|
filter: grayscale(1);
|
||||||
|
width: 50px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.my-orgs {
|
||||||
|
display: flex;
|
||||||
|
gap: 1em;
|
||||||
|
font-size: 80%;
|
||||||
|
.logo {
|
||||||
|
display: block;
|
||||||
|
filter: grayscale(1);
|
||||||
|
width: 50px;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue