This commit is contained in:
2023-02-20 10:20:12 +10:00
parent 9961aba160
commit ccc30a8b7a
30 changed files with 1026 additions and 646 deletions

View File

@@ -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>

View File

@@ -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 {

View File

@@ -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();

View File

@@ -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;
}

View File

@@ -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 {

View File

@@ -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,
},

View File

@@ -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;

View File

@@ -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;

View File

@@ -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";
}
}