🔨 Send settings

This commit is contained in:
SebClem 2022-06-18 02:28:38 +02:00
parent f8d174224c
commit f6baab77ed
Signed by: sebclem
GPG Key ID: 5A4308F6A359EA50
10 changed files with 180 additions and 34 deletions

View File

@ -8,7 +8,6 @@
"typescript.inlayHints.propertyDeclarationTypes.enabled": true, "typescript.inlayHints.propertyDeclarationTypes.enabled": true,
"typescript.inlayHints.parameterNames.suppressWhenArgumentMatchesName": true, "typescript.inlayHints.parameterNames.suppressWhenArgumentMatchesName": true,
"typescript.inlayHints.parameterNames.enabled": "literals", "typescript.inlayHints.parameterNames.enabled": "literals",
"typescript.inlayHints.parameterTypes.enabled": true,
"javascript.inlayHints.enumMemberValues.enabled": true, "javascript.inlayHints.enumMemberValues.enabled": true,
"javascript.inlayHints.propertyDeclarationTypes.enabled": true, "javascript.inlayHints.propertyDeclarationTypes.enabled": true,
"javascript.inlayHints.parameterNames.suppressWhenArgumentMatchesName": true, "javascript.inlayHints.parameterNames.suppressWhenArgumentMatchesName": true,

View File

@ -3,12 +3,14 @@
v-for="(data, index) of snacks" v-for="(data, index) of snacks"
:key="data.uuid" :key="data.uuid"
v-model="data.snack" v-model="data.snack"
top="true" location="top start"
left="true"
:style="{ 'margin-top': calcMargin(index) }" :style="{ 'margin-top': calcMargin(index) }"
:color="data.color" :color="data.color"
> >
<div>{{ data.text }}</div> <div>
<v-icon class="mr-2">{{ data.icon }}</v-icon>
{{ data.text }}
</div>
<template v-slot:actions> <template v-slot:actions>
<v-btn <v-btn
variant="text" variant="text"
@ -30,7 +32,7 @@ function calcMargin(i: number) {
} }
let snacks = ref< let snacks = ref<
{ text: string; color: string; snack: boolean; uuid: string }[] { text: string; color: string; snack: boolean; uuid: string; icon: string }[]
>([]); >([]);
eventQueuStore.$subscribe((mutation, state) => { eventQueuStore.$subscribe((mutation, state) => {
@ -42,6 +44,7 @@ eventQueuStore.$subscribe((mutation, state) => {
text: event.text, text: event.text,
color: event.type, color: event.type,
uuid: event.uuid, uuid: event.uuid,
icon: getIcon(event.type),
}); });
} }
} }
@ -66,9 +69,23 @@ if (eventQueuStore.size != 0) {
text: event.text, text: event.text,
color: event.type, color: event.type,
uuid: event.uuid, uuid: event.uuid,
icon: getIcon(event.type),
}); });
} }
} }
function getIcon(type: string) {
switch (type) {
case "success":
return "mdi-check";
case "warning":
return "mdi-alert";
case "error":
return "mdi-alert-decagram";
default:
return "mdi-alert-circle";
}
}
</script> </script>
<style scoped></style> <style scoped></style>

View File

@ -12,13 +12,7 @@
></bool-field-component> ></bool-field-component>
</v-col> </v-col>
</v-row> </v-row>
<template <template v-if="isOn">
v-if="
!!props.descriptor.mainField
? settingStoreRef.values.value[props.descriptor.mainField.id]
: true
"
>
<v-row v-for="item of props.descriptor.fields" :key="item.id"> <v-row v-for="item of props.descriptor.fields" :key="item.id">
<v-col> <v-col>
<bool-field-component <bool-field-component
@ -26,10 +20,12 @@
:fieldDescription="item" :fieldDescription="item"
></bool-field-component> ></bool-field-component>
<select-field-component <select-field-component
:required="isOn"
:fieldDescription="item" :fieldDescription="item"
v-if="isSelect(item.type)" v-if="isSelect(item.type)"
></select-field-component> ></select-field-component>
<text-field-component <text-field-component
:required="isOn"
:fieldDescription="item" :fieldDescription="item"
v-if="item.type == 'STRING'" v-if="item.type == 'STRING'"
></text-field-component> ></text-field-component>
@ -45,6 +41,7 @@
<script setup lang="ts"> <script setup lang="ts">
import type { SettingDescrition } from "@/data/Setting/SettingDescription"; import type { SettingDescrition } from "@/data/Setting/SettingDescription";
import { useSettingStore } from "@/stores/setting"; import { useSettingStore } from "@/stores/setting";
import { computed } from "@vue/reactivity";
import { storeToRefs } from "pinia"; import { storeToRefs } from "pinia";
import boolFieldComponent from "./fields/BoolFieldComponent.vue"; import boolFieldComponent from "./fields/BoolFieldComponent.vue";
import SelectFieldComponent from "./fields/SelectFieldComponent.vue"; import SelectFieldComponent from "./fields/SelectFieldComponent.vue";
@ -65,6 +62,12 @@ function isSelect(type: string) {
return false; return false;
} }
} }
const isOn = computed(() => {
return props.descriptor.mainField
? !!settingStoreRef.values.value[props.descriptor.mainField.id]
: true;
});
</script> </script>
<style scoped></style> <style scoped></style>

View File

@ -14,14 +14,30 @@
width="15" width="15"
></v-progress-circular> ></v-progress-circular>
</v-overlay> </v-overlay>
<v-row> <v-form ref="form">
<v-col <v-row>
v-for="item of settingsDescription" <v-col
:key="item.name" v-for="item of settingsDescription"
md="6" :key="item.name"
cols="12" md="6"
> cols="12"
<setting-group-component :descriptor="item"></setting-group-component> >
<setting-group-component :descriptor="item"></setting-group-component>
</v-col>
</v-row>
</v-form>
<v-row justify="center">
<v-col cols="1">
<v-btn
v-on:click="send"
color="success"
prepend-icon="mdi-content-save"
block
:disabled="preventSave"
v-if="settingsDescription.length > 0"
>
Save
</v-btn>
</v-col> </v-col>
</v-row> </v-row>
</div> </div>
@ -39,10 +55,12 @@ import {
import { import {
getSettingDescrition, getSettingDescrition,
getSettingValues, getSettingValues,
sendSetting,
} from "@/services/settingsService"; } from "@/services/settingsService";
import { useEventQueuStore } from "@/stores/eventQueu";
import { useSettingStore } from "@/stores/setting"; import { useSettingStore } from "@/stores/setting";
import { storeToRefs } from "pinia"; import { storeToRefs } from "pinia";
import { ref } from "vue"; import { ref, watch } from "vue";
import SettingGroupComponent from "./SettingGroupComponent.vue"; import SettingGroupComponent from "./SettingGroupComponent.vue";
const props = defineProps<{ guild: Guild }>(); const props = defineProps<{ guild: Guild }>();
@ -50,8 +68,10 @@ const props = defineProps<{ guild: Guild }>();
const settingStore = useSettingStore(); const settingStore = useSettingStore();
const { values, loading, text_channels, voice_channels, roles } = const { values, loading, text_channels, voice_channels, roles } =
storeToRefs(settingStore); storeToRefs(settingStore);
loading.value = true; loading.value = true;
values.value = {}; values.value = {};
const settingsDescription = ref([] as SettingDescrition[]); const settingsDescription = ref([] as SettingDescrition[]);
getSettingDescrition().then((value) => { getSettingDescrition().then((value) => {
@ -86,11 +106,59 @@ function loadSettings() {
for (let item of value) { for (let item of value) {
temp[item.id] = item.value; temp[item.id] = item.value;
} }
values.value = temp; values.value = temp;
settingStore.savedValues = JSON.parse(JSON.stringify(temp));
loading.value = false; loading.value = false;
}); });
} }
const form = ref(null);
const preventSave = ref(false);
watch(
values,
() => {
const theForm = form.value as any;
theForm?.validate().then((value: any) => {
preventSave.value =
!value.valid ||
JSON.stringify(values.value) ==
JSON.stringify(settingStore.savedValues);
});
},
{ deep: true }
);
function send() {
const theForm = form.value as any;
theForm?.validate().then((value: any) => {
if (value.valid) {
loading.value = true;
sendSetting(
props.guild.id,
settingStore.values,
settingsDescription.value
)
.then((saved) => {
let temp = {} as SettingValue;
for (let item of saved) {
temp[item.id] = item.value;
}
values.value = temp;
settingStore.savedValues = JSON.parse(JSON.stringify(temp));
loading.value = false;
const eventQueuStore = useEventQueuStore();
eventQueuStore.push({
uuid: undefined,
type: "success",
text: "Settings saved",
});
})
.catch(() => {
loading.value = false;
});
}
});
}
</script> </script>
<style scoped></style> <style scoped></style>

View File

@ -14,6 +14,7 @@
:items="items?.value" :items="items?.value"
variant="outlined" variant="outlined"
v-model="values[fieldDescription.id]" v-model="values[fieldDescription.id]"
:rules="[required]"
></v-autocomplete> ></v-autocomplete>
</v-col> </v-col>
</v-row> </v-row>
@ -24,10 +25,19 @@ import type { FieldDescriptor } from "@/data/Setting/SettingDescription";
import { useSettingStore } from "@/stores/setting"; import { useSettingStore } from "@/stores/setting";
import { computed } from "@vue/reactivity"; import { computed } from "@vue/reactivity";
import { storeToRefs } from "pinia"; import { storeToRefs } from "pinia";
const props = defineProps<{ fieldDescription: FieldDescriptor }>(); const props = defineProps<{
fieldDescription: FieldDescriptor;
required: boolean;
}>();
const settingStore = useSettingStore(); const settingStore = useSettingStore();
function required(value: string) {
return props.required
? !!values.value[props.fieldDescription.id] || "Required"
: true;
}
const { loading, roles, text_channels, voice_channels, values } = const { loading, roles, text_channels, voice_channels, values } =
storeToRefs(settingStore); storeToRefs(settingStore);
const loadingComp = computed(() => { const loadingComp = computed(() => {

View File

@ -11,6 +11,7 @@
:loading="loading" :loading="loading"
variant="outlined" variant="outlined"
v-model="settingStore.values[fieldDescription.id]" v-model="settingStore.values[fieldDescription.id]"
:rules="[required]"
></v-text-field> ></v-text-field>
</v-col> </v-col>
</v-row> </v-row>
@ -20,11 +21,18 @@
import type { FieldDescriptor } from "@/data/Setting/SettingDescription"; import type { FieldDescriptor } from "@/data/Setting/SettingDescription";
import { useSettingStore } from "@/stores/setting"; import { useSettingStore } from "@/stores/setting";
import { storeToRefs } from "pinia"; import { storeToRefs } from "pinia";
defineProps<{ fieldDescription: FieldDescriptor }>(); const props = defineProps<{
fieldDescription: FieldDescriptor;
required: boolean;
}>();
const settingStore = useSettingStore(); const settingStore = useSettingStore();
const { loading } = storeToRefs(settingStore); const { loading } = storeToRefs(settingStore);
function required(value: string) {
return props.required ? !!value || "Required" : true;
}
</script> </script>
<style scoped></style> <style scoped></style>

View File

@ -35,13 +35,6 @@ async function login(code: string): Promise<boolean> {
console.log("Loggin fail !"); console.log("Loggin fail !");
console.log(reason); console.log(reason);
logout(false, true); logout(false, true);
const eventQueuStore = useEventQueuStore();
eventQueuStore.push({
uuid: undefined,
type: "error",
text: "Login fail, Please try aguain.",
});
return false; return false;
} }
} }

View File

@ -1,5 +1,6 @@
import type { RawSettingValue } from "@/data/Setting/RawSettingValue"; import type { RawSettingValue } from "@/data/Setting/RawSettingValue";
import type { SettingDescrition } from "@/data/Setting/SettingDescription"; import type { SettingDescrition } from "@/data/Setting/SettingDescription";
import type { SettingValue } from "@/data/Setting/SettingValue";
import { useEventQueuStore } from "@/stores/eventQueu"; import { useEventQueuStore } from "@/stores/eventQueu";
import { useUserStore } from "@/stores/user"; import { useUserStore } from "@/stores/user";
import axios from "axios"; import axios from "axios";
@ -46,4 +47,52 @@ function getSettingValues(guildId: string): Promise<RawSettingValue[]> {
}); });
} }
export { getSettingDescrition, getSettingValues }; function sendSetting(
guildId: string,
data: SettingValue,
description: SettingDescrition[]
): Promise<RawSettingValue[]> {
return new Promise((resolve, reject) => {
const userStore = useUserStore();
const converted = buildValuePayload(data, description);
axios
.post<RawSettingValue[]>(`/setting/${guildId}/values`, converted, {
headers: {
authorization: `Bearer ${userStore.token}`,
},
})
.then((value) => {
resolve(value.data);
})
.catch((reason) => {
console.error(`Fail to save settings !`);
console.log(reason);
const eventQueuStore = useEventQueuStore();
eventQueuStore.push({
uuid: undefined,
type: "error",
text: "Fail to save settings !",
});
reject(reason);
});
});
}
function buildValuePayload(
data: SettingValue,
description: SettingDescrition[]
): RawSettingValue[] {
const temp = [] as RawSettingValue[];
for (const item of description) {
if (item.mainField) {
temp.push({ id: item.mainField.id, value: data[item.mainField.id] });
}
for (const field of item.fields) {
temp.push({ id: field.id, value: data[field.id] });
}
}
return temp;
}
export { getSettingDescrition, getSettingValues, sendSetting };

View File

@ -4,6 +4,7 @@ import { defineStore } from "pinia";
export const useSettingStore = defineStore({ export const useSettingStore = defineStore({
id: "setting", id: "setting",
state: () => ({ state: () => ({
savedValues: {} as SettingValue,
values: {} as SettingValue, values: {} as SettingValue,
text_channels: [] as SettingValue[], text_channels: [] as SettingValue[],
voice_channels: [] as SettingValue[], voice_channels: [] as SettingValue[],

View File

@ -13,12 +13,10 @@
<script setup lang="ts"> <script setup lang="ts">
import { login } from "@/services/authService"; import { login } from "@/services/authService";
import { useUserStore } from "@/stores/user";
import { useRoute, useRouter } from "vue-router"; import { useRoute, useRouter } from "vue-router";
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
const userStore = useUserStore();
if (route.query.code) { if (route.query.code) {
login(route.query.code as string).then(() => { login(route.query.code as string).then(() => {
router.push("/"); router.push("/");