✨ Sticker CRUD
This commit is contained in:
		| @@ -19,26 +19,32 @@ | ||||
|       <tr> | ||||
|         <td>{{ item.id }}</td> | ||||
|         <td> | ||||
|           <v-img | ||||
|             cover | ||||
|             aspect-ratio="1" | ||||
|             width="28" | ||||
|             height="28" | ||||
|             color="grey-lighten-2" | ||||
|             rounded="sm" | ||||
|             :src="`${config.public.solarNetworkApi}/cgi/uc/attachments/${item.attachment.rid}`" | ||||
|           > | ||||
|             <template #placeholder> | ||||
|               <div class="d-flex align-center justify-center fill-height"> | ||||
|                 <v-progress-circular | ||||
|                   size="x-small" | ||||
|                   width="3" | ||||
|                   color="grey-lighten-4" | ||||
|                   indeterminate | ||||
|                 ></v-progress-circular> | ||||
|               </div> | ||||
|             </template> | ||||
|           </v-img> | ||||
|           <div class="item-texture-cell"> | ||||
|             <v-img | ||||
|               cover | ||||
|               aspect-ratio="1" | ||||
|               width="28" | ||||
|               height="28" | ||||
|               color="grey-lighten-2" | ||||
|               rounded="sm" | ||||
|               :src="`${config.public.solarNetworkApi}/cgi/uc/attachments/${item.attachment.rid}`" | ||||
|             > | ||||
|               <template #placeholder> | ||||
|                 <div class="d-flex align-center justify-center fill-height"> | ||||
|                   <v-progress-circular | ||||
|                     size="x-small" | ||||
|                     width="3" | ||||
|                     color="grey-lighten-4" | ||||
|                     indeterminate | ||||
|                   ></v-progress-circular> | ||||
|                 </div> | ||||
|               </template> | ||||
|             </v-img> | ||||
|  | ||||
|             <v-code class="px-2 w-fit font-mono"> | ||||
|               {{ item.attachment.rid }} | ||||
|             </v-code> | ||||
|           </div> | ||||
|         </td> | ||||
|         <td>{{ item.name }}</td> | ||||
|         <td>{{ props.packPrefix + item.alias }}</td> | ||||
| @@ -49,7 +55,47 @@ | ||||
|             size="x-small" | ||||
|             color="warning" | ||||
|             icon="mdi-pencil" | ||||
|             class="ms-[-8px]" | ||||
|             :to="`/creator/stickers/${item.pack_id}/${item.id}/edit`" | ||||
|           /> | ||||
|  | ||||
|           <v-dialog max-width="480"> | ||||
|             <template #activator="{ props }"> | ||||
|               <v-btn | ||||
|                 v-bind="props" | ||||
|                 variant="text" | ||||
|                 size="x-small" | ||||
|                 color="error" | ||||
|                 icon="mdi-delete" | ||||
|                 :disabled="submitting" | ||||
|               /> | ||||
|             </template> | ||||
|  | ||||
|             <template v-slot:default="{ isActive }"> | ||||
|               <v-card :title="`Delete sticker #${item.id}?`"> | ||||
|                 <v-card-text> | ||||
|                   This action will delete this sticker, all content used it will no longer show your sticker. | ||||
|                   But the attachment will still exists. | ||||
|                 </v-card-text> | ||||
|  | ||||
|                 <v-card-actions> | ||||
|                   <v-spacer></v-spacer> | ||||
|  | ||||
|                   <v-btn | ||||
|                     text="Cancel" | ||||
|                     color="grey" | ||||
|                     @click="isActive.value = false" | ||||
|                   ></v-btn> | ||||
|  | ||||
|                   <v-btn | ||||
|                     text="Delete" | ||||
|                     color="error" | ||||
|                     @click="() => { deleteSticker(item); isActive.value = false }" | ||||
|                   /> | ||||
|                 </v-card-actions> | ||||
|               </v-card> | ||||
|             </template> | ||||
|           </v-dialog> | ||||
|         </td> | ||||
|       </tr> | ||||
|     </template> | ||||
| @@ -107,4 +153,30 @@ async function readStickers({ page, itemsPerPage }: { page?: number; itemsPerPag | ||||
| } | ||||
|  | ||||
| onMounted(() => readStickers({})) | ||||
|  | ||||
| const submitting = ref(false) | ||||
|  | ||||
| async function deleteSticker(item: any) { | ||||
|   submitting.value = true | ||||
|  | ||||
|   const res = await solarFetch(`/cgi/uc/stickers/${item.id}`, { | ||||
|     method: "DELETE", | ||||
|   }) | ||||
|   if (res.status !== 200) { | ||||
|     error.value = await res.text() | ||||
|   } else { | ||||
|     await readStickers({}) | ||||
|   } | ||||
|  | ||||
|   submitting.value = false | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style scoped> | ||||
| .item-texture-cell { | ||||
|   display: grid; | ||||
|   grid-template-columns: 28px auto; | ||||
|   gap: 6px; | ||||
|   width: fit-content; | ||||
| } | ||||
| </style> | ||||
|   | ||||
							
								
								
									
										178
									
								
								pages/creator/stickers/[id]/[sticker]/edit.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										178
									
								
								pages/creator/stickers/[id]/[sticker]/edit.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,178 @@ | ||||
| <template> | ||||
|   <v-container class="px-12"> | ||||
|     <div class="flex justify-between items-center mt-5"> | ||||
|       <div class="flex items-end gap-2"> | ||||
|         <h1 class="text-2xl">Edit sticker: {{ data?.name ?? "Loading" }}</h1> | ||||
|       </div> | ||||
|  | ||||
|       <div class="flex gap-2"> | ||||
|         <v-btn | ||||
|           color="grey" | ||||
|           text="Cancel" | ||||
|           prepend-icon="mdi-arrow-left" | ||||
|           variant="tonal" | ||||
|           to="/creator/stickers" | ||||
|         /> | ||||
|       </div> | ||||
|     </div> | ||||
|  | ||||
|     <v-expand-transition> | ||||
|       <v-alert v-if="error" variant="tonal" type="error" class="text-xs mt-5 mb-3"> | ||||
|         {{ t("errorOccurred", [error]) }} | ||||
|       </v-alert> | ||||
|     </v-expand-transition> | ||||
|  | ||||
|     <v-form class="mt-5" @submit.prevent="submit"> | ||||
|       <v-row> | ||||
|         <v-col cols="12"> | ||||
|           <v-card title="Pack info" prepend-icon="mdi-sticker-emoji" density="compact"> | ||||
|             <v-card-text class="mt-2"> | ||||
|               <p class="text-lg"><b>{{ pack?.name ?? "Loading..." }}</b></p> | ||||
|               <p>{{ pack?.description ?? "Please stand by..." }}</p> | ||||
|             </v-card-text> | ||||
|           </v-card> | ||||
|         </v-col> | ||||
|         <v-col cols="12" md="6"> | ||||
|           <v-text-field | ||||
|             label="Name" | ||||
|             name="name" | ||||
|             variant="outlined" | ||||
|             persistent-hint | ||||
|             hint="A human friendly name for user to recognize this sticker" | ||||
|             v-model="stickerName" | ||||
|           /> | ||||
|         </v-col> | ||||
|         <v-col cols="12" md="6"> | ||||
|           <v-text-field | ||||
|             label="Alias" | ||||
|             name="alias" | ||||
|             variant="outlined" | ||||
|             persistent-hint | ||||
|             hint="A placeholder of this sticker, will prepend pack's prefix" | ||||
|             v-model="stickerAlias" | ||||
|           > | ||||
|             <template #prepend-inner> | ||||
|               <p class="ms-1 me-[-5px] text-grey">{{ pack?.prefix }}</p> | ||||
|             </template> | ||||
|           </v-text-field> | ||||
|         </v-col> | ||||
|         <v-col cols="12"> | ||||
|           <v-text-field | ||||
|             label="Attachment" | ||||
|             name="attachment_id" | ||||
|             variant="outlined" | ||||
|             persistent-hint | ||||
|             v-model="attachmentRid" | ||||
|           > | ||||
|             <template #details> | ||||
|               <p class="order-first v-messages"> | ||||
|                 The texture / image of this sticker, you can upload one from | ||||
|                 <nuxt-link to="/gallery/new?pool=c3RpY2tlcg" target="_blank" class="underline">here</nuxt-link> | ||||
|               </p> | ||||
|             </template> | ||||
|  | ||||
|             <template #prepend-inner> | ||||
|               <v-img | ||||
|                 cover | ||||
|                 aspect-ratio="1" | ||||
|                 width="28" | ||||
|                 height="28" | ||||
|                 color="grey-lighten-2" | ||||
|                 rounded="sm" | ||||
|                 :src="attachmentRid.length > 0 ? `${config.public.solarNetworkApi}/cgi/uc/attachments/${attachmentRid}` : `example.com/not-found`" | ||||
|               > | ||||
|                 <template #placeholder> | ||||
|                   <div class="d-flex align-center justify-center fill-height" v-if="attachmentRid.length > 0"> | ||||
|                     <v-progress-circular | ||||
|                       size="x-small" | ||||
|                       width="3" | ||||
|                       color="grey-lighten-4" | ||||
|                       indeterminate | ||||
|                     ></v-progress-circular> | ||||
|                   </div> | ||||
|                   <div class="d-flex align-center justify-center fill-height" v-else> | ||||
|                     <v-icon icon="mdi-image-broken-variant" class="block" size="18" /> | ||||
|                   </div> | ||||
|                 </template> | ||||
|               </v-img> | ||||
|             </template> | ||||
|           </v-text-field> | ||||
|         </v-col> | ||||
|       </v-row> | ||||
|  | ||||
|       <div class="flex justify-end"> | ||||
|         <v-btn type="submit" text="Save changes" append-icon="mdi-content-save" :disabled="data == null" | ||||
|                :loading="submitting" /> | ||||
|       </div> | ||||
|     </v-form> | ||||
|   </v-container> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| definePageMeta({ | ||||
|   layout: "creator-hub", | ||||
|   middleware: ["auth"], | ||||
| }) | ||||
|  | ||||
| useHead({ | ||||
|   title: "Edit Sticker", | ||||
| }) | ||||
|  | ||||
|  | ||||
| const { t } = useI18n() | ||||
| const route = useRoute() | ||||
| const config = useRuntimeConfig() | ||||
|  | ||||
| const data = ref<any>(null) | ||||
| const pack = ref<any>(null) | ||||
|  | ||||
| const attachmentRid = ref<string>("") | ||||
| const stickerName = ref<string>("") | ||||
| const stickerAlias = ref<string>("") | ||||
|  | ||||
| async function readPack() { | ||||
|   const res = await solarFetch(`/cgi/uc/stickers/packs/${route.params.id}`) | ||||
|   if (res.status != 200) { | ||||
|     error.value = await res.text() | ||||
|   } else { | ||||
|     pack.value = await res.json() | ||||
|   } | ||||
| } | ||||
|  | ||||
| async function readSticker() { | ||||
|   const res = await solarFetch(`/cgi/uc/stickers/${route.params.sticker}`) | ||||
|   if (res.status != 200) { | ||||
|     error.value = await res.text() | ||||
|   } else { | ||||
|     data.value = await res.json() | ||||
|     stickerName.value = data.value?.name | ||||
|     stickerAlias.value = data.value?.alias | ||||
|     attachmentRid.value = data.value?.attachment.rid | ||||
|   } | ||||
| } | ||||
|  | ||||
| onMounted(() => Promise.all([readPack(), readSticker()])) | ||||
|  | ||||
| const error = ref<null | string>(null) | ||||
| const submitting = ref(false) | ||||
|  | ||||
| async function submit(evt: SubmitEvent) { | ||||
|   const data = Object.fromEntries(new FormData(evt.target as HTMLFormElement).entries()) | ||||
|   if (!data.name) return | ||||
|  | ||||
|   submitting.value = true | ||||
|  | ||||
|   const res = await solarFetch(`/cgi/uc/stickers/packs/${route.params.id}`, { | ||||
|     method: "PUT", | ||||
|     headers: { "Content-Type": "application/json" }, | ||||
|     body: JSON.stringify(data), | ||||
|   }) | ||||
|   if (res.status != 200) { | ||||
|     error.value = await res.text() | ||||
|   } else { | ||||
|     navigateTo("/creator/stickers") | ||||
|   } | ||||
|  | ||||
|   submitting.value = false | ||||
| } | ||||
| </script> | ||||
							
								
								
									
										158
									
								
								pages/creator/stickers/[id]/new.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										158
									
								
								pages/creator/stickers/[id]/new.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,158 @@ | ||||
| <template> | ||||
|   <v-container class="px-12"> | ||||
|     <div class="flex justify-between items-center mt-5"> | ||||
|       <div class="flex items-end gap-2"> | ||||
|         <h1 class="text-2xl">Create a new sticker</h1> | ||||
|       </div> | ||||
|  | ||||
|       <div class="flex gap-2"> | ||||
|         <v-btn | ||||
|           color="grey" | ||||
|           text="Cancel" | ||||
|           prepend-icon="mdi-arrow-left" | ||||
|           variant="tonal" | ||||
|           to="/creator/stickers" | ||||
|         /> | ||||
|       </div> | ||||
|     </div> | ||||
|  | ||||
|     <v-expand-transition> | ||||
|       <v-alert v-if="error" variant="tonal" type="error" class="text-xs mt-5 mb-3"> | ||||
|         {{ t("errorOccurred", [error]) }} | ||||
|       </v-alert> | ||||
|     </v-expand-transition> | ||||
|  | ||||
|     <v-form class="mt-5" @submit.prevent="submit"> | ||||
|       <v-row> | ||||
|         <v-col cols="12"> | ||||
|           <v-card title="Pack info" prepend-icon="mdi-sticker-emoji" density="compact"> | ||||
|             <v-card-text class="mt-2"> | ||||
|               <p class="text-lg"><b>{{ data?.name ?? "Loading..." }}</b></p> | ||||
|               <p>{{ data?.description ?? "Please stand by..." }}</p> | ||||
|             </v-card-text> | ||||
|           </v-card> | ||||
|         </v-col> | ||||
|         <v-col cols="12" md="6"> | ||||
|           <v-text-field | ||||
|             label="Name" | ||||
|             name="name" | ||||
|             variant="outlined" | ||||
|             persistent-hint | ||||
|             hint="A human friendly name for user to recognize this sticker" | ||||
|           /> | ||||
|         </v-col> | ||||
|         <v-col cols="12" md="6"> | ||||
|           <v-text-field | ||||
|             label="Alias" | ||||
|             name="alias" | ||||
|             variant="outlined" | ||||
|             persistent-hint | ||||
|             hint="A placeholder of this sticker, will prepend pack's prefix" | ||||
|           /> | ||||
|         </v-col> | ||||
|         <v-col cols="12"> | ||||
|           <v-text-field | ||||
|             label="Attachment" | ||||
|             name="attachment_id" | ||||
|             variant="outlined" | ||||
|             persistent-hint | ||||
|             v-model="attachmentRid" | ||||
|           > | ||||
|             <template #details> | ||||
|               <p class="order-first v-messages"> | ||||
|                 The texture / image of this sticker, you can upload one from | ||||
|                 <nuxt-link to="/gallery/new?pool=c3RpY2tlcg" target="_blank" class="underline">here</nuxt-link> | ||||
|               </p> | ||||
|             </template> | ||||
|  | ||||
|             <template #prepend-inner> | ||||
|               <v-img | ||||
|                 cover | ||||
|                 aspect-ratio="1" | ||||
|                 width="28" | ||||
|                 height="28" | ||||
|                 color="grey-lighten-2" | ||||
|                 rounded="sm" | ||||
|                 :src="attachmentRid.length > 0 ? `${config.public.solarNetworkApi}/cgi/uc/attachments/${attachmentRid}` : `example.com/not-found`" | ||||
|               > | ||||
|                 <template #placeholder> | ||||
|                   <div class="d-flex align-center justify-center fill-height" v-if="attachmentRid.length > 0"> | ||||
|                     <v-progress-circular | ||||
|                       size="x-small" | ||||
|                       width="3" | ||||
|                       color="grey-lighten-4" | ||||
|                       indeterminate | ||||
|                     ></v-progress-circular> | ||||
|                   </div> | ||||
|                   <div class="d-flex align-center justify-center fill-height" v-else> | ||||
|                     <v-icon icon="mdi-image-broken-variant" class="block" size="18"/> | ||||
|                   </div> | ||||
|                 </template> | ||||
|               </v-img> | ||||
|             </template> | ||||
|           </v-text-field> | ||||
|         </v-col> | ||||
|       </v-row> | ||||
|  | ||||
|       <div class="flex justify-end"> | ||||
|         <v-btn type="submit" text="Create" append-icon="mdi-plus" :disabled="data == null" :loading="submitting" /> | ||||
|       </div> | ||||
|     </v-form> | ||||
|   </v-container> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| definePageMeta({ | ||||
|   layout: "creator-hub", | ||||
|   middleware: ["auth"], | ||||
| }) | ||||
|  | ||||
| useHead({ | ||||
|   title: "New Sticker", | ||||
| }) | ||||
|  | ||||
| const { t } = useI18n() | ||||
| const route = useRoute() | ||||
| const config = useRuntimeConfig() | ||||
|  | ||||
| const attachmentRid = ref<string>("") | ||||
|  | ||||
| const data = ref<any>(null) | ||||
|  | ||||
| async function readPack() { | ||||
|   const res = await solarFetch(`/cgi/uc/stickers/packs/${route.params.id}`) | ||||
|   if (res.status != 200) { | ||||
|     error.value = await res.text() | ||||
|   } else { | ||||
|     data.value = await res.json() | ||||
|   } | ||||
| } | ||||
|  | ||||
| onMounted(() => readPack()) | ||||
|  | ||||
| const error = ref<null | string>(null) | ||||
| const submitting = ref(false) | ||||
|  | ||||
| async function submit(evt: SubmitEvent) { | ||||
|   const data = Object.fromEntries(new FormData(evt.target as HTMLFormElement).entries()) | ||||
|   if (!data.name) return | ||||
|  | ||||
|   submitting.value = true | ||||
|  | ||||
|   const res = await solarFetch("/cgi/uc/stickers", { | ||||
|     method: "POST", | ||||
|     headers: { "Content-Type": "application/json" }, | ||||
|     body: JSON.stringify({ | ||||
|       pack_id: route.params.id, | ||||
|       ...data, | ||||
|     }), | ||||
|   }) | ||||
|   if (res.status != 200) { | ||||
|     error.value = await res.text() | ||||
|   } else { | ||||
|     navigateTo("/creator/stickers") | ||||
|   } | ||||
|  | ||||
|   submitting.value = false | ||||
| } | ||||
| </script> | ||||
| @@ -51,12 +51,19 @@ | ||||
|               <v-col cols="12" md="6" lg="4"> | ||||
|                 <p><b>Actions</b></p> | ||||
|                 <div class="flex mx-[-10px]"> | ||||
|                   <v-btn | ||||
|                     variant="text" | ||||
|                     size="x-small" | ||||
|                     color="info" | ||||
|                     icon="mdi-sticker-plus" | ||||
|                     :to="`/creator/stickers/${item.id}/new`" | ||||
|                   /> | ||||
|                   <v-btn | ||||
|                     variant="text" | ||||
|                     size="x-small" | ||||
|                     color="warning" | ||||
|                     icon="mdi-pencil" | ||||
|                     :to="`/creator/stickers/edit/${item.id}`" | ||||
|                     :to="`/creator/stickers/${item.id}/edit`" | ||||
|                   /> | ||||
|  | ||||
|                   <v-dialog max-width="480"> | ||||
|   | ||||
| @@ -45,6 +45,7 @@ | ||||
|                       size="x-small" | ||||
|                       color="info" | ||||
|                       icon="mdi-key" | ||||
|                       class="ms-[-8px]" | ||||
|                     /> | ||||
|                   </template> | ||||
|                 </dev-bot-token-dialog> | ||||
|   | ||||
| @@ -10,7 +10,7 @@ | ||||
|  | ||||
|           <v-select | ||||
|             label="Storage pool" | ||||
|             variant="underlined" | ||||
|             variant="solo" | ||||
|             :items="poolOptions" | ||||
|             item-title="label" | ||||
|             item-value="value" | ||||
| @@ -80,10 +80,21 @@ useHead({ | ||||
| }) | ||||
|  | ||||
| const { t } = useI18n() | ||||
| const route = useRoute() | ||||
|  | ||||
| onMounted(() => { | ||||
|   if (route.query.pool) { | ||||
|     pool.value = atob(decodeURIComponent(route.query.pool.toString())) | ||||
|     if (pool.value == "dedicated") { | ||||
|       pool.value = poolOptions[0].value | ||||
|     } | ||||
|   } | ||||
| }) | ||||
|  | ||||
| const poolOptions = [ | ||||
|   { label: "Interactive", description: "Public indexable, no lifecycle.", value: "interactive" }, | ||||
|   { label: "Messaging", description: "Has lifecycle, will delete after 14 days.", value: "messaging" }, | ||||
|   { label: "Sticker", description: "Public indexable, privilege required.", value: "sticker", disabled: true }, | ||||
|   { label: "Dedicated Pool", description: "Your own configuration, coming soon.", value: "dedicated", disabled: true }, | ||||
| ] | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user