updates
This commit is contained in:
@@ -129,31 +129,31 @@ const handleClickItem = (item: string) => {
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
ul {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
background-color: #f9f9f9;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
li {
|
||||
padding: 12px 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
li:hover {
|
||||
background-color: #f1f1f1;
|
||||
}
|
||||
}
|
||||
|
||||
// New content here
|
||||
.dropdown {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
ul {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
background-color: #f9f9f9;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
li {
|
||||
padding: 12px 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
li:hover {
|
||||
background-color: #f1f1f1;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -56,7 +56,7 @@ const handleMouseLeave = () => {
|
||||
|
||||
const handleSlidePrev = () => {
|
||||
if (currentSlide.value == 0) {
|
||||
currentSlide.value = maxSlide;
|
||||
currentSlide.value = maxSlide.value;
|
||||
} else {
|
||||
currentSlide.value--;
|
||||
}
|
||||
@@ -165,10 +165,12 @@ const disconnectMutationObserver = () => {
|
||||
|
||||
.carousel-slide-prev {
|
||||
left: 1rem;
|
||||
filter: drop-shadow(0px 0px 2px rgba(0, 0, 0, 1));
|
||||
}
|
||||
|
||||
.carousel-slide-next {
|
||||
right: 1rem;
|
||||
filter: drop-shadow(0px 0px 2px rgba(0, 0, 0, 1));
|
||||
}
|
||||
|
||||
.carousel-slide-indicators {
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue";
|
||||
import { api } from "../helpers/api";
|
||||
import { ApiMedia } from "../helpers/api.types";
|
||||
import { imageLoad } from "../helpers/image";
|
||||
import SMButton from "./SMButton.vue";
|
||||
import SMLoadingIcon from "./SMLoadingIcon.vue";
|
||||
|
||||
@@ -53,17 +55,20 @@ const props = defineProps({
|
||||
},
|
||||
});
|
||||
|
||||
let imageUrl = ref(null);
|
||||
let imageUrl = ref("");
|
||||
|
||||
const handleLoad = async () => {
|
||||
try {
|
||||
let result = await api.get(`/media/${props.image}`);
|
||||
if (result.json.medium) {
|
||||
imageUrl.value = result.json.medium.url;
|
||||
const handleLoad = () => {
|
||||
imageUrl.value = "";
|
||||
|
||||
api.get(`/media/${props.image}`).then((result) => {
|
||||
const data = result.data as ApiMedia;
|
||||
|
||||
if (data && data.medium) {
|
||||
imageLoad(data.medium.url, (url) => {
|
||||
imageUrl.value = url;
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
imageUrl.value = "";
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
handleLoad();
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
'dialog',
|
||||
{ 'dialog-narrow': narrow },
|
||||
{ 'dialog-full': full },
|
||||
{ 'dialog-noshadow': noShadow },
|
||||
]">
|
||||
<transition name="fade">
|
||||
<div v-if="loading" class="dialog-loading-cover">
|
||||
@@ -37,6 +38,10 @@ defineProps({
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
noShadow: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -54,6 +59,10 @@ defineProps({
|
||||
min-width: map-get($spacer, 5) * 12;
|
||||
box-shadow: 4px 4px 20px rgba(0, 0, 0, 0.5);
|
||||
|
||||
&.dialog-noshadow {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
& > h1 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,8 @@
|
||||
type == 'email' ||
|
||||
type == 'password' ||
|
||||
type == 'email' ||
|
||||
type == 'url'
|
||||
type == 'url' ||
|
||||
type == 'daterange'
|
||||
"
|
||||
:type="type"
|
||||
:placeholder="placeholder"
|
||||
@@ -130,8 +131,8 @@ const objForm = inject("form", props.form);
|
||||
const objControl =
|
||||
!isEmpty(objForm) && props.control != "" ? objForm[props.control] : null;
|
||||
|
||||
const label = ref("");
|
||||
const feedbackInvalid = ref("");
|
||||
const label = ref(props.label);
|
||||
const feedbackInvalid = ref(props.feedbackInvalid);
|
||||
|
||||
watch(
|
||||
() => props.label,
|
||||
@@ -238,6 +239,7 @@ const inline = computed(() => {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: map-get($spacer, 4);
|
||||
flex: 1;
|
||||
|
||||
&.sm-input-active {
|
||||
label {
|
||||
|
||||
@@ -73,7 +73,7 @@ const menuItems = [
|
||||
name: "workshops",
|
||||
label: "Workshops",
|
||||
to: "/workshops",
|
||||
icon: "shapes-outline",
|
||||
icon: "library-outline",
|
||||
},
|
||||
// {
|
||||
// name: "courses",
|
||||
@@ -107,7 +107,7 @@ const menuItems = [
|
||||
name: "dashboard",
|
||||
label: "Dashboard",
|
||||
to: "/dashboard",
|
||||
icon: "apps-outline",
|
||||
icon: "grid-outline",
|
||||
show: () => userStore.id,
|
||||
inNav: false,
|
||||
},
|
||||
|
||||
@@ -13,18 +13,23 @@
|
||||
class="sm-page"
|
||||
:style="styleObject">
|
||||
<slot></slot>
|
||||
<SMContainer v-if="slots.container"
|
||||
><slot name="container"></slot
|
||||
></SMContainer>
|
||||
</div>
|
||||
</SMLoader>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useUserStore } from "../store/UserStore";
|
||||
import { useSlots } from "vue";
|
||||
import SMLoader from "./SMLoader.vue";
|
||||
import SMErrorForbidden from "./errors/Forbidden.vue";
|
||||
import SMErrorInternal from "./errors/Internal.vue";
|
||||
import SMErrorNotFound from "./errors/NotFound.vue";
|
||||
import SMBreadcrumbs from "../components/SMBreadcrumbs.vue";
|
||||
import { useUserStore } from "../store/UserStore";
|
||||
import SMContainer from "./SMContainer.vue";
|
||||
|
||||
const props = defineProps({
|
||||
pageError: {
|
||||
@@ -53,6 +58,8 @@ const props = defineProps({
|
||||
required: false,
|
||||
},
|
||||
});
|
||||
|
||||
const slots = useSlots();
|
||||
const userStore = useUserStore();
|
||||
let styleObject = {};
|
||||
|
||||
@@ -74,7 +81,7 @@ const hasPermission = () => {
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
margin-bottom: calc(map-get($spacer, 5) * 2);
|
||||
padding-bottom: calc(map-get($spacer, 5) * 2);
|
||||
|
||||
&.sm-no-breadcrumbs {
|
||||
margin-bottom: 0;
|
||||
|
||||
@@ -46,8 +46,10 @@ import {
|
||||
stripHtmlTags,
|
||||
} from "../helpers/common";
|
||||
import { format } from "date-fns";
|
||||
import SMButton from "./SMButton.vue";
|
||||
import { api } from "../helpers/api";
|
||||
import { imageLoad } from "../helpers/image";
|
||||
import SMButton from "./SMButton.vue";
|
||||
import { ApiMedia } from "../helpers/api.types";
|
||||
|
||||
const props = defineProps({
|
||||
title: {
|
||||
@@ -149,15 +151,15 @@ const hideImageLoader = computed(() => {
|
||||
|
||||
onMounted(async () => {
|
||||
if (imageUrl.value && imageUrl.value.length > 0 && isUUID(imageUrl.value)) {
|
||||
try {
|
||||
let result = await api.get(`/media/${props.image}`);
|
||||
api.get(`/media/${props.image}`).then((result) => {
|
||||
const data = result.data as ApiMedia;
|
||||
|
||||
if (result.json.medium) {
|
||||
imageUrl.value = result.json.medium.url;
|
||||
if (data && data.medium) {
|
||||
imageLoad(data.medium.url, (url) => {
|
||||
imageUrl.value = url;
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
/* empty */
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -168,6 +170,7 @@ onMounted(async () => {
|
||||
flex-direction: column;
|
||||
border: 1px solid $border-color;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 0 28px rgba(0, 0, 0, 0.05);
|
||||
max-width: 21rem;
|
||||
width: 100%;
|
||||
@@ -227,6 +230,7 @@ onMounted(async () => {
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
padding: 0 map-get($spacer, 3) map-get($spacer, 3) map-get($spacer, 3);
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
@@ -241,7 +245,7 @@ onMounted(async () => {
|
||||
font-size: 80%;
|
||||
margin-bottom: 0.4rem;
|
||||
|
||||
svg {
|
||||
ion-icon {
|
||||
flex: 0 1 1rem;
|
||||
margin-right: map-get($spacer, 1);
|
||||
padding-top: 0.1rem;
|
||||
|
||||
@@ -2,36 +2,39 @@
|
||||
<SMModal>
|
||||
<SMDialog :loading="formLoading">
|
||||
<h1>Change Password</h1>
|
||||
<SMMessage
|
||||
v-if="isSuccessful"
|
||||
type="success"
|
||||
message="Your password has been changed successfully" />
|
||||
<SMInput
|
||||
v-if="!isSuccessful"
|
||||
v-model="formData.password.value"
|
||||
type="password"
|
||||
label="New Password"
|
||||
required
|
||||
:error="formData.password.error" />
|
||||
<SMFormFooter>
|
||||
<template v-if="!isSuccessful" #left>
|
||||
<SMButton
|
||||
type="secondary"
|
||||
label="Cancel"
|
||||
@click="handleCancel()" />
|
||||
</template>
|
||||
<template #right>
|
||||
<SMButton
|
||||
type="primary"
|
||||
:label="btnConfirm"
|
||||
@click="handleConfirm()" />
|
||||
</template>
|
||||
</SMFormFooter>
|
||||
<SMForm v-model="form">
|
||||
<SMMessage
|
||||
v-if="isSuccessful"
|
||||
type="success"
|
||||
message="Your password has been changed successfully" />
|
||||
<SMInput
|
||||
v-if="!isSuccessful"
|
||||
control="password"
|
||||
type="password"
|
||||
label="New Password" />
|
||||
<SMFormFooter>
|
||||
<template v-if="!isSuccessful" #left>
|
||||
<SMButton
|
||||
type="secondary"
|
||||
label="Cancel"
|
||||
@click="handleCancel()" />
|
||||
</template>
|
||||
<template #right>
|
||||
<SMButton
|
||||
type="primary"
|
||||
:label="btnConfirm"
|
||||
@click="handleConfirm()" />
|
||||
</template>
|
||||
</SMFormFooter>
|
||||
</SMForm>
|
||||
</SMDialog>
|
||||
</SMModal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { api } from "../../helpers/api";
|
||||
import { FormControl } from "../../helpers/form";
|
||||
import { And, Required, Password } from "../../helpers/validate";
|
||||
import { useUserStore } from "../../store/UserStore";
|
||||
import { ref, reactive, computed, onMounted, onUnmounted } from "vue";
|
||||
import { closeDialog } from "vue3-promise-dialog";
|
||||
@@ -42,20 +45,9 @@ import SMButton from "../SMButton.vue";
|
||||
import SMFormFooter from "../SMFormFooter.vue";
|
||||
import SMInput from "../SMInput.vue";
|
||||
|
||||
const formData = reactive({
|
||||
password: {
|
||||
value: "",
|
||||
error: "",
|
||||
rules: {
|
||||
required: true,
|
||||
required_message: "A password is needed",
|
||||
min: 8,
|
||||
min_message: "Your password needs to be at least %d characters",
|
||||
password: "special",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const controlPassword = reactive(
|
||||
FormControl("", And([Required(), Password()]))
|
||||
);
|
||||
const userStore = useUserStore();
|
||||
const formLoading = ref(false);
|
||||
const isSuccessful = ref(false);
|
||||
@@ -72,18 +64,20 @@ const handleConfirm = async () => {
|
||||
if (isSuccessful.value == true) {
|
||||
closeDialog(true);
|
||||
} else {
|
||||
const valid = controlPassword.validate();
|
||||
|
||||
try {
|
||||
formLoading.value = true;
|
||||
await api.put({
|
||||
url: `/users/${userStore.id}`,
|
||||
body: {
|
||||
password: formData.password.value,
|
||||
password: controlPassword.value,
|
||||
},
|
||||
});
|
||||
|
||||
isSuccessful.value = true;
|
||||
} catch (err) {
|
||||
formData.password.error =
|
||||
controlPassword.error =
|
||||
err.json?.message || "An unexpected error occurred";
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user