mirror of
https://github.com/hay-kot/homebox.git
synced 2024-12-25 16:26:31 +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");
|
||||
}
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const username = ref("");
|
||||
const email = ref("");
|
||||
const groupName = ref("");
|
||||
const password = ref("");
|
||||
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() {
|
||||
loading.value = true;
|
||||
const { error } = await api.register({
|
||||
|
@ -29,6 +51,7 @@
|
|||
email: email.value,
|
||||
password: password.value,
|
||||
groupName: groupName.value,
|
||||
token: groupToken.value,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
|
@ -42,6 +65,13 @@
|
|||
registerForm.value = false;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
console.log(groupToken.value);
|
||||
if (groupToken.value !== "") {
|
||||
registerForm.value = true;
|
||||
}
|
||||
});
|
||||
|
||||
const loading = ref(false);
|
||||
const loginPassword = ref("");
|
||||
|
||||
|
@ -57,6 +87,7 @@
|
|||
|
||||
toast.success("Logged in successfully");
|
||||
|
||||
// @ts-expect-error - expires is either a date or a string, need to figure out store typing
|
||||
authStore.$patch({
|
||||
token: data.token,
|
||||
expires: data.expiresAt,
|
||||
|
@ -122,7 +153,13 @@
|
|||
</h2>
|
||||
<FormTextField v-model="email" label="Set your email?" />
|
||||
<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" />
|
||||
<PasswordScore v-model:valid="canRegister" :password="password" />
|
||||
<div class="card-actions justify-end">
|
||||
|
|
|
@ -144,25 +144,12 @@
|
|||
name: "Email",
|
||||
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[];
|
||||
});
|
||||
|
||||
const api = useUserApi();
|
||||
const confirm = useConfirm();
|
||||
const notify = useNotifier();
|
||||
|
||||
async function deleteProfile() {
|
||||
const result = await confirm.open(
|
||||
|
@ -173,7 +160,37 @@
|
|||
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>
|
||||
|
||||
|
@ -182,23 +199,28 @@
|
|||
<BaseCard>
|
||||
<template #title>
|
||||
<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>
|
||||
<template #description> Invite users, and manage your account. </template>
|
||||
</BaseSectionHeader>
|
||||
</template>
|
||||
|
||||
<DetailsSection :details="details">
|
||||
<template #invitation>
|
||||
<BaseButton class="ml-auto" size="sm"> Generate Invite Link </BaseButton>
|
||||
</template>
|
||||
<template #change-password>
|
||||
<BaseButton class="ml-auto" size="sm"> Change Password </BaseButton>
|
||||
</template>
|
||||
<template #delete-profile>
|
||||
<BaseButton class="ml-auto btn-error" size="sm" @click="deleteProfile"> Delete Profile </BaseButton>
|
||||
</template>
|
||||
</DetailsSection>
|
||||
<DetailsSection :details="details" />
|
||||
|
||||
<div class="p-4">
|
||||
<div class="flex gap-2">
|
||||
<BaseButton size="sm"> Change Password </BaseButton>
|
||||
<BaseButton size="sm" @click="generateToken"> Generate Invite Link </BaseButton>
|
||||
</div>
|
||||
<div v-if="token" class="pt-4 flex items-center pl-1">
|
||||
<CopyText class="mr-2 btn-primary" :text="tokenUrl" />
|
||||
{{ tokenUrl }}
|
||||
</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>
|
||||
|
@ -251,6 +273,20 @@
|
|||
</div>
|
||||
</div>
|
||||
</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>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -24,11 +24,7 @@ export const useAuthStore = defineStore("auth", {
|
|||
},
|
||||
actions: {
|
||||
async logout(api: UserClient) {
|
||||
const result = await api.logout();
|
||||
|
||||
if (result.error) {
|
||||
return result;
|
||||
}
|
||||
const result = await api.user.logout();
|
||||
|
||||
this.token = "";
|
||||
this.expires = "";
|
||||
|
|
Loading…
Reference in a new issue