developed

This commit is contained in:
Komek Hayytnazarov 2022-09-20 11:24:21 +05:00
parent 2b4e6b902a
commit 257021fe55
9 changed files with 360 additions and 115 deletions

View File

@ -0,0 +1,77 @@
<template>
<!-- BEGIN: Languages -->
<Dropdown class="intro-x w-8">
<DropdownToggle
tag="div"
role="button"
class="w-6 h-6 rounded-full overflow-hidden shadow-lg image-fit zoom-in scale-110"
>
<img alt="flag" :src="`/src/assets/images/flags/${selectedLang}.png`" />
</DropdownToggle>
<DropdownMenu class="pt-2">
<DropdownContent tag="div">
<div
v-for="(lang, key) in languages"
:key="key"
class="cursor-pointer relative flex items-center"
:class="{ 'mt-5': key }"
@click="onLanguageChanged(lang.code)"
>
<div class="w-5 h-5 flex-none image-fit mr-1">
<img
alt="Icewall Tailwind HTML Admin Template"
class="rounded-full"
:src="`/src/assets/images/flags/${lang.code}.png`"
/>
</div>
<div class="ml-2 overflow-hidden">
<div class="flex items-center">
<a href="javascript:;" class="font-medium truncate mr-5">{{
lang.label
}}</a>
<!-- <a class="font-medium truncate mr-5">Rus Dili</a> -->
</div>
</div>
</div>
</DropdownContent>
</DropdownMenu>
</Dropdown>
<!-- END: Languages -->
</template>
<script setup>
import { ref, onBeforeMount } from "vue";
import { SELECTED_LANG } from "@/helpers";
import i18n from "@/i18n";
const selectedLang = ref("");
const languages = [
{
code: "tm",
label: "Türkmen dili",
},
{
code: "ru",
label: "Русский",
},
{
code: "en",
label: "English",
},
];
const onLanguageChanged = (val) => {
selectedLang.value = val;
i18n.global.locale = val;
localStorage.setItem(SELECTED_LANG, val);
};
onBeforeMount(() => {
const lang = localStorage.getItem(SELECTED_LANG);
if (lang) selectedLang.value = lang;
else selectedLang.value = "tm";
});
</script>

View File

@ -65,49 +65,7 @@
</Dropdown>
<!-- END: Notifications -->
<!-- BEGIN: Languages -->
<Dropdown class="intro-x w-8 mr-4 sm:mr-6">
<DropdownToggle
tag="div"
role="button"
class="w-6 h-6 rounded-full overflow-hidden shadow-lg image-fit zoom-in scale-110"
>
<img
alt="flag"
:src="`/src/assets/images/flags/${selectedLang}.png`"
/>
</DropdownToggle>
<DropdownMenu class="pt-2">
<DropdownContent tag="div">
<div
v-for="(lang, key) in languages"
:key="key"
class="cursor-pointer relative flex items-center"
:class="{ 'mt-5': key }"
@click="onLanguageChanged(lang.code)"
>
<div class="w-5 h-5 flex-none image-fit mr-1">
<img
alt="Icewall Tailwind HTML Admin Template"
class="rounded-full"
:src="`/src/assets/images/flags/${lang.code}.png`"
/>
</div>
<div class="ml-2 overflow-hidden">
<div class="flex items-center">
<a href="javascript:;" class="font-medium truncate mr-5">{{
lang.label
}}</a>
<!-- <a class="font-medium truncate mr-5">Rus Dili</a> -->
</div>
</div>
</div>
</DropdownContent>
</DropdownMenu>
</Dropdown>
<!-- END: Languages -->
<Lang class="mr-4" />
<!-- BEGIN: Account Menu -->
<Dropdown class="intro-x">
@ -163,40 +121,9 @@
</template>
<script setup>
import { ref, onBeforeMount } from "vue";
import { useAuthStore } from "@/stores";
import { SELECTED_LANG } from "@/helpers";
import i18n from "@/i18n";
import Logo from "@/components/logo/Main.vue";
const searchDropdown = ref(false);
const showSearchDropdown = () => {
searchDropdown.value = true;
};
const hideSearchDropdown = () => {
searchDropdown.value = false;
};
const selectedLang = ref("");
const languages = [
{
code: "tm",
label: "Türkmen dili",
},
{
code: "ru",
label: "Русский",
},
{
code: "en",
label: "English",
},
];
const onLanguageChanged = (val) => {
selectedLang.value = val;
i18n.global.locale = val;
localStorage.setItem(SELECTED_LANG, val);
};
import Lang from "@/components/lang/Main.vue";
const onLogout = () => {
console.log("Logout");
@ -204,11 +131,4 @@ const onLogout = () => {
const authStore = useAuthStore();
return authStore.logout();
};
onBeforeMount(() => {
const lang = localStorage.getItem(SELECTED_LANG);
if (lang) selectedLang.value = lang;
else selectedLang.value = "tm";
});
</script>

View File

@ -2,4 +2,5 @@ export const locale = {
GENERAL_REPORT: "General Report",
ERROR: "Error",
SUCCESS: "Success",
SIGN_IN: "Sign In",
};

View File

@ -2,4 +2,5 @@ export const locale = {
GENERAL_REPORT: "Общий отчет",
ERROR: "Ошибка",
SUCCESS: "Успех",
SIGN_IN: "Войти",
};

View File

@ -2,4 +2,5 @@ export const locale = {
GENERAL_REPORT: "Umumy hasabat",
ERROR: "Ýalňyşlyk",
SUCCESS: "Üstünlik",
SIGN_IN: "Giriň",
};

View File

@ -116,7 +116,12 @@ const router = createRouter({
router.beforeEach(async (to) => {
// redirect to login page if not logged in and trying to access a restricted page
const publicPages = ["/login", "/register", "/email-verify"];
const publicPages = [
"/login",
"/register",
"/email-verify",
"/update-password",
];
const authRequired = !publicPages.includes(to.path);
const authStore = useAuthStore();

View File

@ -38,7 +38,7 @@ export const useAuthStore = defineStore({
async register(newUser) {
try {
await fetchWrapper.post(`${baseUrl}/register`, newUser);
// redirect to Email verification page
router.push({ path: "/email-verify" });
} catch (error) {
@ -55,7 +55,10 @@ export const useAuthStore = defineStore({
async verifyEmail(email, token) {
try {
const response = await fetchWrapper.post(`${baseUrl}/verify-email`, { email, token })
const response = await fetchWrapper.post(`${baseUrl}/verify-email`, {
email,
token,
});
// update pinia state
this.user = response["data"];
@ -65,7 +68,6 @@ export const useAuthStore = defineStore({
// redirect to home page
router.push({ path: "/" });
} catch (error) {
// const alertStore = useAlertStore();
// alertStore.error(error);
@ -74,24 +76,43 @@ export const useAuthStore = defineStore({
async updateClient(client) {
try {
const response = await fetchWrapper.post(`${baseUrl}/update-client`, client);
const response = await fetchWrapper.post(
`${baseUrl}/update-client`,
client
);
const updatedClient = response["data"];
updatedClient["token"] = this.user.token
updatedClient["token"] = this.user.token;
// update pinia state
this.user = updatedClient
this.user = updatedClient;
// store user details and jwt in local storage to keep user logged in between page refreshes
localStorage.setItem(USER, JSON.stringify(this.user));
const alertStore = useAlertStore();
// trigger alert
alertStore.success('Successfully updated');
alertStore.success("Successfully updated");
} catch (error) {
// const alertStore = useAlertStore();
// alertStore.error(error);
}
}
},
async forgotPassword(email) {
try {
const response = await fetchWrapper.post(`${baseUrl}/forgot-password`, {
email,
});
console.log("forgot password", response);
// redirect to home page
router.push({ path: "/update-password" });
} catch (error) {
// const alertStore = useAlertStore();
// alertStore.error(error);
}
},
},
});

View File

@ -46,11 +46,15 @@
<div
class="my-auto mx-auto xl:ml-20 bg-white dark:bg-darkmode-600 xl:bg-transparent px-5 sm:px-8 py-8 xl:p-0 rounded-md shadow-md xl:shadow-none w-full sm:w-3/4 lg:w-2/4 xl:w-auto"
>
<h2
class="intro-x font-bold text-2xl xl:text-3xl text-center xl:text-left"
>
Sign In
</h2>
<div class="flex items-center justify-between">
<h2
class="intro-x font-bold text-2xl xl:text-3xl text-center xl:text-left"
>
{{ $t("SIGN_IN") }}
</h2>
<Lang />
</div>
<div class="intro-x mt-2 text-slate-400 xl:hidden text-center">
A few more clicks to sign in to your account. Manage all your
e-commerce accounts in one place
@ -78,6 +82,7 @@
class="intro-x login__input form-control py-3 px-4 block mt-4"
:class="{ 'border-danger': validate.password.$error }"
placeholder="Password"
@keyup.enter="onLogin"
/>
<template v-if="validate.password.$error">
<div
@ -140,13 +145,16 @@
</template>
<script setup>
import { onMounted, reactive, toRefs, ref, watch } from "vue";
import { onMounted, reactive, toRefs, ref, onBeforeMount } from "vue";
import dom from "@left4code/tw-starter/dist/js/dom";
import { useVuelidate } from "@vuelidate/core";
import { required, minLength, email } from "@vuelidate/validators";
import { useAuthStore } from "@/stores";
import router from "@/router";
import Logo from "@/components/logo/Main.vue";
import Lang from "@/components/lang/Main.vue";
import { SELECTED_LANG } from "@/helpers";
import i18n from "@/i18n";
const formData = reactive({
email: "",
@ -174,25 +182,23 @@ const onLogin = async () => {
validate.value.$touch();
// if form is valid
if (!validate.value.$invalid) {
isLoading.value = true;
await authStore.login(formData.email, formData.password);
isLoading.value = false;
}
// if form is invalid
if (validate.value.$invalid) return;
isLoading.value = true;
await authStore.login(formData.email, formData.password);
isLoading.value = false;
};
watch(
() => router.params,
(previous, current) => {
console.log(`${previous} and ${current}`);
},
{
deep: true,
}
);
const onForgotPassword = async () => {
const authStore = useAuthStore();
validate.value.email.$touch();
const onForgotPassword = () => router.push({ path: "/error-page" });
if (validate.value.email.$invalid) return;
console.log("validate email only: ", formData.email);
await authStore.forgotPassword(formData.email);
};
const onRegister = () => router.push({ path: "/register" });

View File

@ -1 +1,214 @@
<template><div>Hello World</div></template>
<template>
<div>
<div class="container sm:px-10">
<div class="block xl:grid grid-cols-2 gap-4">
<!-- BEGIN: Login Info -->
<div class="hidden xl:flex flex-col min-h-screen">
<a href="" class="-intro-x flex items-center pt-5">
<Logo />
<span class="text-white text-lg ml-3">
Türkmenistanyň Döwlet haryt-çig mal biržasy
</span>
</a>
<div class="my-auto">
<img
alt="Midone Tailwind HTML Admin Template"
class="-intro-x w-1/2 -mt-16"
src="@/assets/images/illustration.svg"
/>
<div
class="-intro-x text-white font-medium text-4xl leading-tight mt-10"
>
A few more clicks to <br />
sign in to your account.
</div>
<div
class="-intro-x mt-5 text-lg text-white text-opacity-70 dark:text-slate-400"
>
Manage all your documents in one place
</div>
</div>
</div>
<!-- END: Login Info -->
<!-- BEGIN: Login Form -->
<div class="h-screen xl:h-auto md:flex py-5 xl:py-0 my-10 xl:my-0">
<div class="md:hidden mb-6">
<a>
<Logo class="mx-auto mb-2" />
<div class="text-center text-white text-lg ml-3">
Türkmenistanyň Döwlet haryt-çig mal biržasy
</div>
</a>
</div>
<div
class="my-auto mx-auto xl:ml-20 bg-white dark:bg-darkmode-600 xl:bg-transparent px-5 sm:px-8 py-8 xl:p-0 rounded-md shadow-md xl:shadow-none w-full sm:w-3/4 lg:w-2/4 xl:w-auto"
>
<h2
class="intro-x font-bold text-2xl xl:text-3xl text-center xl:text-left"
>
Update password
</h2>
<div class="intro-x mt-2 text-slate-400 xl:hidden text-center">
A few more clicks to sign in to your account. Manage all your
e-commerce accounts in one place
</div>
<div class="intro-x mt-8">
<input
type="text"
v-model.trim="validate.email.$model"
class="intro-x login__input form-control py-3 px-4 block mt-4"
:class="{ 'border-danger': validate.email.$error }"
placeholder="Email"
/>
<template v-if="validate.email.$error">
<div
v-for="(error, index) in validate.email.$errors"
:key="index"
class="text-danger mt-2"
>
{{ error.$message }}
</div>
</template>
<input
type="text"
v-model.trim="validate.token.$model"
class="intro-x login__input form-control py-3 px-4 block mt-4"
:class="{ 'border-danger': validate.token.$error }"
placeholder="Type token you received in mail"
/>
<template v-if="validate.token.$error">
<div
v-for="(error, index) in validate.token.$errors"
:key="index"
class="text-danger mt-2"
>
{{ error.$message }}
</div>
</template>
<input
type="password"
v-model.trim="validate.password.$model"
class="intro-x login__input form-control py-3 px-4 block mt-4"
:class="{ 'border-danger': validate.password.$error }"
placeholder="Password"
/>
<template v-if="validate.password.$error">
<div
v-for="(error, index) in validate.password.$errors"
:key="index"
class="text-danger mt-2"
>
{{ error.$message }}
</div>
</template>
<input
type="password"
v-model.trim="validate.newPassword.$model"
class="intro-x login__input form-control py-3 px-4 block mt-4"
:class="{ 'border-danger': validate.newPassword.$error }"
placeholder="New password"
/>
<template v-if="validate.newPassword.$error">
<div
v-for="(error, index) in validate.newPassword.$errors"
:key="index"
class="text-danger mt-2"
>
{{ error.$message }}
</div>
</template>
</div>
<div class="intro-x mt-5 xl:mt-8 text-center xl:text-left">
<button
class="btn btn-primary py-3 px-4 w-full xl:w-32 xl:mr-3 align-top"
@click.prevent="onUpdate"
>
Update
<LoadingIcon
icon="oval"
color="white"
class="w-4 h-4 ml-2"
v-if="isLoading"
/>
</button>
<button
class="btn btn-outline-secondary py-3 px-4 w-full xl:w-32 mt-3 xl:mt-0 align-top"
@click.prevent="onLogin"
>
Sign in
</button>
</div>
</div>
</div>
<!-- END: Login Form -->
</div>
</div>
</div>
</template>
<script setup>
import { onMounted, reactive, toRefs, ref, watch } from "vue";
import dom from "@left4code/tw-starter/dist/js/dom";
import { useVuelidate } from "@vuelidate/core";
import { required, minLength, email } from "@vuelidate/validators";
import { useAuthStore } from "@/stores";
import router from "@/router";
import Logo from "@/components/logo/Main.vue";
const formData = reactive({
email: "",
password: "",
newPassword: "",
token: "",
});
const isLoading = ref(false);
const rules = {
email: {
required,
email,
},
password: {
required,
minLength: minLength(8),
},
newPassword: {
required,
minLength: minLength(8),
},
token: {
required,
minLength: minLength(4),
},
};
const validate = useVuelidate(rules, toRefs(formData));
const onLogin = () => router.push({ path: "/login" });
const onUpdate = async () => {
const authStore = useAuthStore();
validate.value.$touch();
// if form is valid
if (!validate.value.$invalid) {
// isLoading.value = true;
// await authStore.login(formData.email, formData.password);
// isLoading.value = false;
}
};
onMounted(() => {
dom("body").removeClass("main").removeClass("error-page").addClass("login");
});
</script>