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",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@pinia/colada": "^0.13.0",
|
||||
"@primevue/forms": "^4.2.2",
|
||||
"@primevue/themes": "^4.2.2",
|
||||
"pinia": "^2.2.6",
|
||||
|
|
@ -1217,6 +1218,14 @@
|
|||
"integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==",
|
||||
"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": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
"format": "prettier --write src/"
|
||||
},
|
||||
"dependencies": {
|
||||
"@pinia/colada": "^0.13.0",
|
||||
"@primevue/forms": "^4.2.2",
|
||||
"@primevue/themes": "^4.2.2",
|
||||
"pinia": "^2.2.6",
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import './assets/main.css'
|
|||
|
||||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
import { PiniaColada } from '@pinia/colada';
|
||||
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
|
|
@ -12,6 +13,7 @@ import StyleClass from 'primevue/styleclass';
|
|||
const app = createApp(App)
|
||||
|
||||
app.use(createPinia())
|
||||
app.use(PiniaColada)
|
||||
app.use(router)
|
||||
app.use(PrimeVue, {
|
||||
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">
|
||||
import { ref, toRaw, watch } from 'vue'
|
||||
import { computed, ref, toRaw, watch } from 'vue'
|
||||
|
||||
import Button from 'primevue/button';
|
||||
import Card from 'primevue/card';
|
||||
import Chip from 'primevue/chip';
|
||||
import ColorPicker from 'primevue/colorpicker';
|
||||
import { Form } from '@primevue/forms';
|
||||
import Image from 'primevue/image';
|
||||
import InputGroup from 'primevue/inputgroup';
|
||||
import InputGroupAddon from 'primevue/inputgroupaddon';
|
||||
import InputText from 'primevue/inputtext';
|
||||
|
|
@ -15,12 +17,16 @@ import IconLayout2Vue from '@/components/layouts/IconLayout2.vue';
|
|||
import IconLayout3Vue from '@/components/layouts/IconLayout3.vue';
|
||||
import IconLayout4Vue from '@/components/layouts/IconLayout4.vue';
|
||||
import Sticker from '@/components/Sticker.vue';
|
||||
import { useSticker } from '@/composables/sticker'
|
||||
import { activist, type Sticker as StickerType } from '@/stores/stickersStore'
|
||||
import { type Organization, useOrganization } from '@/queries/organization';
|
||||
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();
|
||||
let sticker = ref<StickerType>(activist);
|
||||
const orgFilter = ref("");
|
||||
|
||||
// When the original sticker appears, copy it into a local coy.
|
||||
watch(original, (newValue) => {
|
||||
|
|
@ -35,6 +41,22 @@ const resolver = () => {
|
|||
function onFormSubmit() {
|
||||
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>
|
||||
|
||||
<template>
|
||||
|
|
@ -52,6 +74,44 @@ function onFormSubmit() {
|
|||
@submit="onFormSubmit">
|
||||
<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="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">
|
||||
<label for="nickname2">Layout {{sticker.layout}}</label>
|
||||
<div class="flex flex-wrap gap-4">
|
||||
|
|
@ -160,4 +220,39 @@ label {
|
|||
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>
|
||||
|
|
|
|||
Loading…
Reference in a new issue