Browse Source

global toast, be able to update client

pull/1572/head
Bernd Storath 3 months ago
parent
commit
c1a2ce601e
  1. 9
      src/app/app.vue
  2. 2
      src/app/components/ClientCard/Avatar.vue
  3. 3
      src/app/components/base/Toast.vue
  4. 4
      src/app/layouts/setup.vue
  5. 28
      src/app/pages/clients/[id].vue
  6. 7
      src/app/pages/login.vue
  7. 7
      src/app/pages/setup/1.vue
  8. 8
      src/app/pages/setup/4.vue
  9. 8
      src/app/pages/setup/5.vue
  10. 7
      src/app/pages/setup/migrate.vue
  11. 21
      src/app/stores/setup.ts
  12. 26
      src/app/stores/toast.ts
  13. 7
      src/server/api/client/[clientId]/index.post.ts
  14. 5
      src/server/utils/types.ts

9
src/app/app.vue

@ -4,7 +4,9 @@
<NuxtPage />
<ToastViewport
class="fixed bottom-0 right-0 z-[2147483647] m-0 flex w-[390px] max-w-[100vw] list-none flex-col gap-[10px] p-[var(--viewport-padding)] outline-none [--viewport-padding:_25px]"
/>
>
<BaseToast ref="toast" />
</ToastViewport>
</NuxtLayout>
</ToastProvider>
</template>
@ -12,6 +14,11 @@
<script setup lang="ts">
const globalStore = useGlobalStore();
globalStore.setLanguage();
const toast = useToast();
const toastRef = useTemplateRef('toast');
toast.setToast(toastRef);
useHead({
bodyAttrs: {
class: 'bg-gray-50 dark:bg-neutral-800',

2
src/app/components/ClientCard/Avatar.vue

@ -25,6 +25,6 @@
const props = defineProps<{
client: LocalClient;
}>();
// TODO: why is this undefined?
console.log(props.client.avatar);
</script>

3
src/app/components/base/Toast.vue

@ -11,11 +11,12 @@ defineExpose({
publish,
});
// TODO: support multiple types (info, success, error, warning)
const count = reactive<{ title: string; message: string }[]>([]);
function publish(e: { title: string; message: string }) {
count.push({ title: e.title, message: e.message });
console.log(count.length);
}
</script>

4
src/app/layouts/setup.vue

@ -17,13 +17,9 @@
</div>
</PanelBody>
</Panel>
<BaseToast ref="toast" />
</main>
</template>
<script lang="ts" setup>
const setupStore = useSetupStore();
const savedRef = useTemplateRef('toast');
setupStore.setErrorRef(savedRef);
</script>

28
src/app/pages/clients/[id].vue

@ -71,7 +71,9 @@
<script lang="ts" setup>
const authStore = useAuthStore();
authStore.update();
const router = useRouter();
const route = useRoute();
const toast = useToast();
const id = route.params.id as string;
const { data: _data, refresh } = await useFetch(`/api/client/${id}`, {
@ -79,8 +81,30 @@ const { data: _data, refresh } = await useFetch(`/api/client/${id}`, {
});
const data = toRef(_data.value);
function submit() {
console.log(data.value);
async function submit() {
try {
const res = await $fetch(`/api/client/${id}`, {
method: 'post',
body: data.value,
});
toast.showToast({
type: 'success',
title: 'Success',
message: 'Saved',
});
if (!res.success) {
throw new Error('Failed to save');
}
router.push('/');
} catch (e) {
if (e instanceof Error) {
toast.showToast({
type: 'error',
title: 'Error',
message: e.message,
});
}
}
}
async function revert() {

7
src/app/pages/login.vue

@ -74,8 +74,6 @@
:value="$t('signIn')"
/>
</form>
<BaseToast ref="toast" />
</main>
</template>
@ -89,7 +87,7 @@ const remember = ref(false);
const username = ref<null | string>(null);
const password = ref<null | string>(null);
const authStore = useAuthStore();
const toast = useTemplateRef('toast');
const toast = useToast();
async function login(e: Event) {
e.preventDefault();
@ -108,7 +106,8 @@ async function login(e: Event) {
}
} catch (error) {
if (error instanceof FetchError) {
toast.value?.publish({
toast.showToast({
type: 'error',
title: t('error.login'),
message: error.data.message,
});

7
src/app/pages/setup/1.vue

@ -22,17 +22,20 @@ const { t, locale, setLocale } = useI18n();
function handleEventUpdateLang(value: string) {
setLocale(value);
}
const setupStore = useSetupStore();
setupStore.setStep(1);
const router = useRouter();
const toast = useToast();
async function updateLang() {
try {
await setupStore.step1(locale.value);
router.push('/setup/2');
} catch (error) {
if (error instanceof FetchError) {
setupStore.handleError({
toast.showToast({
type: 'error',
title: t('setup.requirements'),
message: error.data.message,
});

8
src/app/pages/setup/4.vue

@ -55,10 +55,13 @@ const username = ref<null | string>(null);
const password = ref<null | string>(null);
const accept = ref<boolean>(true);
const toast = useToast();
async function newAccount() {
try {
if (!username.value || !password.value) {
setupStore.handleError({
toast.showToast({
type: 'error',
title: t('setup.requirements'),
message: t('setup.emptyFields'),
});
@ -69,7 +72,8 @@ async function newAccount() {
await router.push('/setup/5');
} catch (error) {
if (error instanceof FetchError) {
setupStore.handleError({
toast.showToast({
type: 'error',
title: t('setup.requirements'),
message: error.data.message,
});

8
src/app/pages/setup/5.vue

@ -43,9 +43,12 @@ const router = useRouter();
const host = ref<null | string>(null);
const port = ref<number>(51820);
const toast = useToast();
async function updateHostPort() {
if (!host.value || !port.value) {
setupStore.handleError({
toast.showToast({
type: 'error',
title: t('setup.requirements'),
message: t('setup.emptyFields'),
});
@ -57,7 +60,8 @@ async function updateHostPort() {
await router.push('/setup/success');
} catch (error) {
if (error instanceof FetchError) {
setupStore.handleError({
toast.showToast({
type: 'error',
title: t('setup.requirements'),
message: error.data.message,
});

7
src/app/pages/setup/migrate.vue

@ -37,10 +37,12 @@ function onChangeFile(evt: Event) {
}
const router = useRouter();
const toast = useToast();
async function sendFile() {
if (!backupFile.value) {
setupStore.handleError({
toast.showToast({
type: 'error',
title: t('setup.requirements'),
message: t('setup.emptyFields'),
});
@ -54,7 +56,8 @@ async function sendFile() {
await router.push('/setup/success');
} catch (error) {
if (error instanceof FetchError) {
setupStore.handleError({
toast.showToast({
type: 'error',
title: t('setup.requirements'),
message: error.data.message,
});

21
src/app/stores/setup.ts

@ -45,25 +45,6 @@ export const useSetupStore = defineStore('Setup', () => {
return response.success;
}
type SetupError = {
title: string;
message: string;
};
type ErrorRef = {
value: { publish: (e: SetupError) => void } | null;
};
const errorRef = ref<null | ErrorRef>(null);
function setErrorRef(a: ErrorRef | null) {
errorRef.value = a;
}
function handleError(e: SetupError) {
errorRef.value?.value?.publish(e);
}
const step = ref(1);
const totalSteps = ref(6);
function setStep(i: number) {
@ -75,8 +56,6 @@ export const useSetupStore = defineStore('Setup', () => {
step4,
step5,
runMigration,
setErrorRef,
handleError,
step,
totalSteps,
setStep,

26
src/app/stores/toast.ts

@ -0,0 +1,26 @@
export const useToast = defineStore('Toast', () => {
type ToastInterface = {
publish: (e: { title: string; message: string }) => void;
};
type ToastRef = Ref<null | ToastInterface>;
const toast = ref<Ref<ToastRef> | null>(null);
function setToast(toastInstance: ToastRef) {
toast.value = toastInstance;
}
function showToast({
title,
message,
}: {
type: 'success' | 'error';
title: string;
message: string;
}) {
toast.value?.value?.publish({ title, message });
}
return { setToast, showToast };
});

7
src/server/api/client/[clientId]/index.post.ts

@ -3,13 +3,16 @@ export default defineEventHandler(async (event) => {
event,
validateZod(clientIdType)
);
const data = await readValidatedBody(
event,
validateZod(clientUpdateType, event)
);
await WireGuard.updateClient({
clientId,
client: { ...data, expiresAt: data.expiresAt ?? null },
client: data,
});
return;
return { success: true };
});

5
src/server/utils/types.ts

@ -3,8 +3,6 @@ import { z, ZodError } from 'zod';
import type { H3Event, EventHandlerRequest } from 'h3';
import { LOCALES } from '#shared/locales';
// TODO: make objects strict
const objectMessage = 'zod.body';
const safeStringRefine = z
@ -153,8 +151,7 @@ const address6 = z
.min(1, { message: 'zod.address6Min' })
.pipe(safeStringRefine);
/** expects formdata, strict */
export const clientUpdateType = z.strictObject({
export const clientUpdateType = z.object({
name: name,
enabled: z.boolean(),
expiresAt: expireDate,

Loading…
Cancel
Save