mirror of
https://github.com/hay-kot/homebox.git
synced 2024-11-26 10:35:40 +00:00
implement UI for url and join
This commit is contained in:
parent
505099ee26
commit
76c6d25850
4 changed files with 141 additions and 34 deletions
38
frontend/components/global/CopyText.vue
Normal file
38
frontend/components/global/CopyText.vue
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
<template>
|
||||||
|
<button class="btn btn-outline btn-square btn-sm" @click="copyText">
|
||||||
|
<label
|
||||||
|
class="swap swap-rotate"
|
||||||
|
:class="{
|
||||||
|
'swap-active': copied,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<Icon class="swap-off h-5 w-5" name="mdi-content-copy" />
|
||||||
|
<Icon class="swap-on h-5 w-5" name="mdi-clipboard" />
|
||||||
|
</label>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const props = defineProps({
|
||||||
|
text: {
|
||||||
|
type: String as () => string,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const copied = ref(false);
|
||||||
|
|
||||||
|
const { copy } = useClipboard();
|
||||||
|
|
||||||
|
function copyText() {
|
||||||
|
copy(props.text);
|
||||||
|
copied.value = true;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
copied.value = false;
|
||||||
|
console.log(copied.value);
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped></style>
|
|
@ -16,12 +16,34 @@
|
||||||
navigateTo("/home");
|
navigateTo("/home");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
const username = ref("");
|
const username = ref("");
|
||||||
const email = ref("");
|
const email = ref("");
|
||||||
const groupName = ref("");
|
const groupName = ref("");
|
||||||
const password = ref("");
|
const password = ref("");
|
||||||
const canRegister = ref(false);
|
const canRegister = ref(false);
|
||||||
|
|
||||||
|
const groupToken = computed<string>({
|
||||||
|
get() {
|
||||||
|
const params = route.query.token;
|
||||||
|
|
||||||
|
if (typeof params === "string") {
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
},
|
||||||
|
set(v) {
|
||||||
|
router.push({
|
||||||
|
query: {
|
||||||
|
token: v,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
async function registerUser() {
|
async function registerUser() {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const { error } = await api.register({
|
const { error } = await api.register({
|
||||||
|
@ -29,6 +51,7 @@
|
||||||
email: email.value,
|
email: email.value,
|
||||||
password: password.value,
|
password: password.value,
|
||||||
groupName: groupName.value,
|
groupName: groupName.value,
|
||||||
|
token: groupToken.value,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
|
@ -42,6 +65,13 @@
|
||||||
registerForm.value = false;
|
registerForm.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
console.log(groupToken.value);
|
||||||
|
if (groupToken.value !== "") {
|
||||||
|
registerForm.value = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const loginPassword = ref("");
|
const loginPassword = ref("");
|
||||||
|
|
||||||
|
@ -57,6 +87,7 @@
|
||||||
|
|
||||||
toast.success("Logged in successfully");
|
toast.success("Logged in successfully");
|
||||||
|
|
||||||
|
// @ts-expect-error - expires is either a date or a string, need to figure out store typing
|
||||||
authStore.$patch({
|
authStore.$patch({
|
||||||
token: data.token,
|
token: data.token,
|
||||||
expires: data.expiresAt,
|
expires: data.expiresAt,
|
||||||
|
@ -122,7 +153,13 @@
|
||||||
</h2>
|
</h2>
|
||||||
<FormTextField v-model="email" label="Set your email?" />
|
<FormTextField v-model="email" label="Set your email?" />
|
||||||
<FormTextField v-model="username" label="What's your name?" />
|
<FormTextField v-model="username" label="What's your name?" />
|
||||||
<FormTextField v-model="groupName" label="Name your group" />
|
<FormTextField v-if="groupToken == ''" v-model="groupName" label="Name your group" />
|
||||||
|
<div v-else class="pt-4 pb-1 text-center">
|
||||||
|
<p>You're Joining an Existing Group!</p>
|
||||||
|
<button type="button" class="text-xs underline" @click="groupToken = ''">
|
||||||
|
Don't Want To Join a Group?
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<FormTextField v-model="password" label="Set your password" type="password" />
|
<FormTextField v-model="password" label="Set your password" type="password" />
|
||||||
<PasswordScore v-model:valid="canRegister" :password="password" />
|
<PasswordScore v-model:valid="canRegister" :password="password" />
|
||||||
<div class="card-actions justify-end">
|
<div class="card-actions justify-end">
|
||||||
|
|
|
@ -144,25 +144,12 @@
|
||||||
name: "Email",
|
name: "Email",
|
||||||
text: auth.self?.email || "Unknown",
|
text: auth.self?.email || "Unknown",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "Invitation Code",
|
|
||||||
text: "",
|
|
||||||
slot: "invitation",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Change Password",
|
|
||||||
text: "",
|
|
||||||
slot: "change-password",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Delete Profile",
|
|
||||||
text: "",
|
|
||||||
slot: "delete-profile",
|
|
||||||
},
|
|
||||||
] as Detail[];
|
] as Detail[];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const api = useUserApi();
|
||||||
const confirm = useConfirm();
|
const confirm = useConfirm();
|
||||||
|
const notify = useNotifier();
|
||||||
|
|
||||||
async function deleteProfile() {
|
async function deleteProfile() {
|
||||||
const result = await confirm.open(
|
const result = await confirm.open(
|
||||||
|
@ -173,7 +160,37 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("delete profile");
|
const { response } = await api.user.delete();
|
||||||
|
|
||||||
|
if (response?.status === 204) {
|
||||||
|
notify.success("Your account has been deleted.");
|
||||||
|
auth.logout(api);
|
||||||
|
navigateTo("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
notify.error("Failed to delete your account.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = ref("");
|
||||||
|
const tokenUrl = computed(() => {
|
||||||
|
if (!window) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${window.location.origin}?token=${token.value}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
async function generateToken() {
|
||||||
|
const date = new Date();
|
||||||
|
|
||||||
|
const { response, data } = await api.group.createInvitation({
|
||||||
|
expiresAt: new Date(date.setDate(date.getDate() + 7)),
|
||||||
|
uses: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response?.status === 201) {
|
||||||
|
token.value = data.token;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -182,23 +199,28 @@
|
||||||
<BaseCard>
|
<BaseCard>
|
||||||
<template #title>
|
<template #title>
|
||||||
<BaseSectionHeader>
|
<BaseSectionHeader>
|
||||||
<Icon name="mdi-fill" class="mr-2 text-base-600" />
|
<Icon name="mdi-account" class="mr-2 -mt-1 text-base-600" />
|
||||||
<span class="text-base-600"> User Profile </span>
|
<span class="text-base-600"> User Profile </span>
|
||||||
<template #description> Invite users, and manage your account. </template>
|
<template #description> Invite users, and manage your account. </template>
|
||||||
</BaseSectionHeader>
|
</BaseSectionHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<DetailsSection :details="details">
|
<DetailsSection :details="details" />
|
||||||
<template #invitation>
|
|
||||||
<BaseButton class="ml-auto" size="sm"> Generate Invite Link </BaseButton>
|
<div class="p-4">
|
||||||
</template>
|
<div class="flex gap-2">
|
||||||
<template #change-password>
|
<BaseButton size="sm"> Change Password </BaseButton>
|
||||||
<BaseButton class="ml-auto" size="sm"> Change Password </BaseButton>
|
<BaseButton size="sm" @click="generateToken"> Generate Invite Link </BaseButton>
|
||||||
</template>
|
</div>
|
||||||
<template #delete-profile>
|
<div v-if="token" class="pt-4 flex items-center pl-1">
|
||||||
<BaseButton class="ml-auto btn-error" size="sm" @click="deleteProfile"> Delete Profile </BaseButton>
|
<CopyText class="mr-2 btn-primary" :text="tokenUrl" />
|
||||||
</template>
|
{{ tokenUrl }}
|
||||||
</DetailsSection>
|
</div>
|
||||||
|
<div v-if="token" class="pt-4 flex items-center pl-1">
|
||||||
|
<CopyText class="mr-2 btn-primary" :text="token" />
|
||||||
|
{{ token }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</BaseCard>
|
</BaseCard>
|
||||||
|
|
||||||
<BaseCard>
|
<BaseCard>
|
||||||
|
@ -251,6 +273,20 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</BaseCard>
|
</BaseCard>
|
||||||
|
|
||||||
|
<BaseCard>
|
||||||
|
<template #title>
|
||||||
|
<BaseSectionHeader>
|
||||||
|
<Icon name="mdi-delete" class="mr-2 -mt-1 text-base-600" />
|
||||||
|
<span class="text-base-600"> Delete Account</span>
|
||||||
|
<template #description> Delete your account and all it's associated data </template>
|
||||||
|
</BaseSectionHeader>
|
||||||
|
|
||||||
|
<div class="py-4 border-t-2 border-gray-300">
|
||||||
|
<BaseButton class="btn-error" @click="deleteProfile"> Delete Account </BaseButton>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</BaseCard>
|
||||||
</BaseContainer>
|
</BaseContainer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -24,11 +24,7 @@ export const useAuthStore = defineStore("auth", {
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
async logout(api: UserClient) {
|
async logout(api: UserClient) {
|
||||||
const result = await api.logout();
|
const result = await api.user.logout();
|
||||||
|
|
||||||
if (result.error) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.token = "";
|
this.token = "";
|
||||||
this.expires = "";
|
this.expires = "";
|
||||||
|
|
Loading…
Reference in a new issue