diff --git a/.vscode/settings.json b/.vscode/settings.json
index ffab266..4c7a313 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -8,7 +8,6 @@
"typescript.inlayHints.propertyDeclarationTypes.enabled": true,
"typescript.inlayHints.parameterNames.suppressWhenArgumentMatchesName": true,
"typescript.inlayHints.parameterNames.enabled": "literals",
- "typescript.inlayHints.parameterTypes.enabled": true,
"javascript.inlayHints.enumMemberValues.enabled": true,
"javascript.inlayHints.propertyDeclarationTypes.enabled": true,
"javascript.inlayHints.parameterNames.suppressWhenArgumentMatchesName": true,
diff --git a/src/components/Header/SnackbarComponent.vue b/src/components/Header/SnackbarComponent.vue
index c617dd8..7aee7fe 100644
--- a/src/components/Header/SnackbarComponent.vue
+++ b/src/components/Header/SnackbarComponent.vue
@@ -3,12 +3,14 @@
v-for="(data, index) of snacks"
:key="data.uuid"
v-model="data.snack"
- top="true"
- left="true"
+ location="top start"
:style="{ 'margin-top': calcMargin(index) }"
:color="data.color"
>
-
{{ data.text }}
+
+ {{ data.icon }}
+ {{ data.text }}
+
([]);
eventQueuStore.$subscribe((mutation, state) => {
@@ -42,6 +44,7 @@ eventQueuStore.$subscribe((mutation, state) => {
text: event.text,
color: event.type,
uuid: event.uuid,
+ icon: getIcon(event.type),
});
}
}
@@ -66,9 +69,23 @@ if (eventQueuStore.size != 0) {
text: event.text,
color: event.type,
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";
+ }
+}
diff --git a/src/components/guild/Settings/SettingGroupComponent.vue b/src/components/guild/Settings/SettingGroupComponent.vue
index b9c78d7..3cc6bf7 100644
--- a/src/components/guild/Settings/SettingGroupComponent.vue
+++ b/src/components/guild/Settings/SettingGroupComponent.vue
@@ -12,13 +12,7 @@
>
-
+
@@ -45,6 +41,7 @@
diff --git a/src/components/guild/Settings/SettingListComponent.vue b/src/components/guild/Settings/SettingListComponent.vue
index e5e6f03..3128421 100644
--- a/src/components/guild/Settings/SettingListComponent.vue
+++ b/src/components/guild/Settings/SettingListComponent.vue
@@ -14,14 +14,30 @@
width="15"
>
-
-
-
+
+
+
+
+
+
+
+
+
+
+ Save
+
@@ -39,10 +55,12 @@ import {
import {
getSettingDescrition,
getSettingValues,
+ sendSetting,
} from "@/services/settingsService";
+import { useEventQueuStore } from "@/stores/eventQueu";
import { useSettingStore } from "@/stores/setting";
import { storeToRefs } from "pinia";
-import { ref } from "vue";
+import { ref, watch } from "vue";
import SettingGroupComponent from "./SettingGroupComponent.vue";
const props = defineProps<{ guild: Guild }>();
@@ -50,8 +68,10 @@ const props = defineProps<{ guild: Guild }>();
const settingStore = useSettingStore();
const { values, loading, text_channels, voice_channels, roles } =
storeToRefs(settingStore);
+
loading.value = true;
values.value = {};
+
const settingsDescription = ref([] as SettingDescrition[]);
getSettingDescrition().then((value) => {
@@ -86,11 +106,59 @@ function loadSettings() {
for (let item of value) {
temp[item.id] = item.value;
}
-
values.value = temp;
+ settingStore.savedValues = JSON.parse(JSON.stringify(temp));
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;
+ });
+ }
+ });
+}
diff --git a/src/components/guild/Settings/fields/SelectFieldComponent.vue b/src/components/guild/Settings/fields/SelectFieldComponent.vue
index 246f714..65179b0 100644
--- a/src/components/guild/Settings/fields/SelectFieldComponent.vue
+++ b/src/components/guild/Settings/fields/SelectFieldComponent.vue
@@ -14,6 +14,7 @@
:items="items?.value"
variant="outlined"
v-model="values[fieldDescription.id]"
+ :rules="[required]"
>
@@ -24,10 +25,19 @@ import type { FieldDescriptor } from "@/data/Setting/SettingDescription";
import { useSettingStore } from "@/stores/setting";
import { computed } from "@vue/reactivity";
import { storeToRefs } from "pinia";
-const props = defineProps<{ fieldDescription: FieldDescriptor }>();
+const props = defineProps<{
+ fieldDescription: FieldDescriptor;
+ required: boolean;
+}>();
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 } =
storeToRefs(settingStore);
const loadingComp = computed(() => {
diff --git a/src/components/guild/Settings/fields/TextFieldComponent.vue b/src/components/guild/Settings/fields/TextFieldComponent.vue
index 844ed86..bb67bc3 100644
--- a/src/components/guild/Settings/fields/TextFieldComponent.vue
+++ b/src/components/guild/Settings/fields/TextFieldComponent.vue
@@ -11,6 +11,7 @@
:loading="loading"
variant="outlined"
v-model="settingStore.values[fieldDescription.id]"
+ :rules="[required]"
>
@@ -20,11 +21,18 @@
import type { FieldDescriptor } from "@/data/Setting/SettingDescription";
import { useSettingStore } from "@/stores/setting";
import { storeToRefs } from "pinia";
-defineProps<{ fieldDescription: FieldDescriptor }>();
+const props = defineProps<{
+ fieldDescription: FieldDescriptor;
+ required: boolean;
+}>();
const settingStore = useSettingStore();
const { loading } = storeToRefs(settingStore);
+
+function required(value: string) {
+ return props.required ? !!value || "Required" : true;
+}
diff --git a/src/services/authService.ts b/src/services/authService.ts
index c2da45c..3d8ddf0 100644
--- a/src/services/authService.ts
+++ b/src/services/authService.ts
@@ -35,13 +35,6 @@ async function login(code: string): Promise {
console.log("Loggin fail !");
console.log(reason);
logout(false, true);
-
- const eventQueuStore = useEventQueuStore();
- eventQueuStore.push({
- uuid: undefined,
- type: "error",
- text: "Login fail, Please try aguain.",
- });
return false;
}
}
diff --git a/src/services/settingsService.ts b/src/services/settingsService.ts
index 5d2c871..0fd4e65 100644
--- a/src/services/settingsService.ts
+++ b/src/services/settingsService.ts
@@ -1,5 +1,6 @@
import type { RawSettingValue } from "@/data/Setting/RawSettingValue";
import type { SettingDescrition } from "@/data/Setting/SettingDescription";
+import type { SettingValue } from "@/data/Setting/SettingValue";
import { useEventQueuStore } from "@/stores/eventQueu";
import { useUserStore } from "@/stores/user";
import axios from "axios";
@@ -46,4 +47,52 @@ function getSettingValues(guildId: string): Promise {
});
}
-export { getSettingDescrition, getSettingValues };
+function sendSetting(
+ guildId: string,
+ data: SettingValue,
+ description: SettingDescrition[]
+): Promise {
+ return new Promise((resolve, reject) => {
+ const userStore = useUserStore();
+
+ const converted = buildValuePayload(data, description);
+ axios
+ .post(`/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 };
diff --git a/src/stores/setting.ts b/src/stores/setting.ts
index 5aea389..05d1b2a 100644
--- a/src/stores/setting.ts
+++ b/src/stores/setting.ts
@@ -4,6 +4,7 @@ import { defineStore } from "pinia";
export const useSettingStore = defineStore({
id: "setting",
state: () => ({
+ savedValues: {} as SettingValue,
values: {} as SettingValue,
text_channels: [] as SettingValue[],
voice_channels: [] as SettingValue[],
diff --git a/src/views/oauth/OauthCallbackView.vue b/src/views/oauth/OauthCallbackView.vue
index e04bba2..e0c4440 100644
--- a/src/views/oauth/OauthCallbackView.vue
+++ b/src/views/oauth/OauthCallbackView.vue
@@ -13,12 +13,10 @@