diff --git a/app/Models/Event.php b/app/Models/Event.php
index eac9a9b..071a834 100644
--- a/app/Models/Event.php
+++ b/app/Models/Event.php
@@ -27,9 +27,11 @@ class Event extends Model
'registration_type',
'registration_data',
'hero',
- 'content'
+ 'content',
+ 'price'
];
+
/**
* Get all of the post's attachments.
*/
diff --git a/database/migrations/2023_02_28_090609_add_price_to_events_table.php b/database/migrations/2023_02_28_090609_add_price_to_events_table.php
new file mode 100644
index 0000000..bee4565
--- /dev/null
+++ b/database/migrations/2023_02_28_090609_add_price_to_events_table.php
@@ -0,0 +1,32 @@
+string('price')->default("");
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::table('events', function (Blueprint $table) {
+ $table->dropColumn('price');
+ });
+ }
+};
diff --git a/resources/js/components/SMPanel.vue b/resources/js/components/SMPanel.vue
index 8e84584..00680bf 100644
--- a/resources/js/components/SMPanel.vue
+++ b/resources/js/components/SMPanel.vue
@@ -19,14 +19,19 @@
-
+ name="calendar-outline"
+ class="icon" />
+
{{ computedDate }}
+
+
$
+
{{ computedPrice }}
+
{{ computedContent }}
@@ -52,7 +57,12 @@ import { api } from "../helpers/api";
import { MediaResponse } from "../helpers/api.types";
import { SMDate } from "../helpers/datetime";
import { imageLoad } from "../helpers/image";
-import { excerpt, replaceHtmlEntites, stripHtmlTags } from "../helpers/string";
+import {
+ excerpt,
+ replaceHtmlEntites,
+ stringToNumber,
+ stripHtmlTags,
+} from "../helpers/string";
import { isUUID } from "../helpers/uuid";
import SMButton from "./SMButton.vue";
@@ -134,6 +144,11 @@ const props = defineProps({
default: "primary",
required: false,
},
+ price: {
+ type: String,
+ default: "",
+ required: false,
+ },
});
let styleObject = reactive({});
@@ -180,14 +195,32 @@ const computedContent = computed(() => {
return excerpt(replaceHtmlEntites(stripHtmlTags(props.content)), 200);
});
+/**
+ * Return a computed day number from props.date
+ */
const computedDay = computed(() => {
return new SMDate(props.date, { format: "yMd" }).format("dd");
});
+/**
+ * Return a computed month name from props.date
+ */
const computedMonth = computed(() => {
return new SMDate(props.date, { format: "yMd" }).format("MMM");
});
+/**
+ * Return a computed price amount, if a form of 0, return "Free"
+ */
+const computedPrice = computed(() => {
+ const parsedPrice = stringToNumber(props.price);
+ if (parsedPrice == 0) {
+ return "Free";
+ }
+
+ return props.price;
+});
+
onMounted(async () => {
if (props.image && props.image.length > 0 && isUUID(props.image)) {
api.get({ url: "/media/{medium}", params: { medium: props.image } })
@@ -290,19 +323,21 @@ watch(
}
.sm-panel-date,
- .sm-panel-location {
+ .sm-panel-location,
+ .sm-panel-price {
display: flex;
flex-direction: row;
align-items: top;
font-size: 80%;
margin-bottom: 0.4rem;
- ion-icon {
+ .icon {
flex: 0 1 1rem;
margin-right: map-get($spacer, 1);
padding-top: 0.1rem;
height: 1rem;
padding: 0.25rem 0;
+ text-align: center;
}
p {
diff --git a/resources/js/helpers/api.types.ts b/resources/js/helpers/api.types.ts
index eddd60c..a8341ff 100644
--- a/resources/js/helpers/api.types.ts
+++ b/resources/js/helpers/api.types.ts
@@ -5,11 +5,13 @@ export interface Event {
content: string;
start_at: string;
end_at: string;
+ publish_at: string;
location: string;
address: string;
status: string;
registration_type: string;
registration_data: string;
+ price: string;
}
export interface EventResponse {
diff --git a/resources/js/helpers/string.ts b/resources/js/helpers/string.ts
index f1421c0..37bd4cd 100644
--- a/resources/js/helpers/string.ts
+++ b/resources/js/helpers/string.ts
@@ -79,3 +79,39 @@ export const replaceHtmlEntites = (txt: string): string => {
return translate[entity];
});
};
+
+/**
+ * Convert a string to a number, ignoring items like dollar signs, etc.
+ *
+ * @param {string} str The string to convert to a number
+ * @returns {number} A number with the minimum amount of decimal places (or 0)
+ */
+export const stringToNumber = (str: string): number => {
+ str = str.replace(/[^\d.-]/g, "");
+ const num = Number.parseFloat(str);
+ return isNaN(num) ? 0 : parseFloat(num.toFixed(2));
+};
+
+/**
+ * Convert a number or string to a price (0 or 0.00).
+ *
+ * @param {number|string} numOrString The number of string to convert to a price.
+ * @returns {string} The converted result.
+ */
+export const toPrice = (numOrString: number | string): string => {
+ let num = 0;
+
+ if (typeof numOrString == "string") {
+ num = stringToNumber(numOrString);
+ } else {
+ num = numOrString;
+ }
+
+ if (num % 1 === 0) {
+ // Number has no decimal places
+ return num.toFixed(0);
+ } else {
+ // Number has decimal places
+ return num.toFixed(2);
+ }
+};
diff --git a/resources/js/views/EventView.vue b/resources/js/views/EventView.vue
index 0b153e5..6a0e5b0 100644
--- a/resources/js/views/EventView.vue
+++ b/resources/js/views/EventView.vue
@@ -61,7 +61,11 @@
label="Register for Event">
-
Date / Time
+
+ Date / Time
+
-
Location
+
+ Location
+
{{
event.location == "online"
@@ -79,6 +87,9 @@
}}
+
+
${{ computedPrice }}
+
@@ -95,6 +106,7 @@ import { api } from "../helpers/api";
import { Event, EventResponse, MediaResponse } from "../helpers/api.types";
import { SMDate } from "../helpers/datetime";
import { imageLoad } from "../helpers/image";
+import { stringToNumber } from "../helpers/string";
import { useApplicationStore } from "../store/ApplicationStore";
const applicationStore = useApplicationStore();
@@ -162,6 +174,18 @@ const workshopDate = computed(() => {
return str;
});
+/**
+ * Return a computed price amount, if a form of 0, return "Free"
+ */
+const computedPrice = computed(() => {
+ const parsedPrice = stringToNumber(event.value.price || "0");
+ if (parsedPrice == 0) {
+ return "Free";
+ }
+
+ return event.value.price;
+});
+
const registerUrl = computed(() => {
let href = "";
@@ -290,10 +314,11 @@ handleLoad();
align-items: center;
height: 1rem;
- ion-icon {
+ .icon {
display: inline-block;
width: 1rem;
margin-right: 0.5rem;
+ text-align: center;
}
}
@@ -329,7 +354,8 @@ handleLoad();
}
.sm-workshop-date,
- .sm-workshop-location {
+ .sm-workshop-location,
+ .sm-workshop-price {
padding: 0 1rem;
}
}
diff --git a/resources/js/views/dashboard/EventEdit.vue b/resources/js/views/dashboard/EventEdit.vue
index 7c18be1..7ff4522 100644
--- a/resources/js/views/dashboard/EventEdit.vue
+++ b/resources/js/views/dashboard/EventEdit.vue
@@ -60,6 +60,11 @@
+
+ Leave blank to hide from public.
+
+
+
{
- form.loading(true);
-
if (route.params.id) {
try {
- let res = await api.get("/events/" + route.params.id);
- if (!res.data.event) {
+ form.loading(true);
+
+ const result = await api.get({
+ url: "/events/{id}",
+ params: { id: route.params.id },
+ });
+ const data = result.data as EventResponse;
+
+ if (!data || !data.event) {
throw new Error("The server is currently not available");
}
- form.controls.title.value = res.data.event.title;
- form.controls.location.value = res.data.event.location;
- form.controls.address.value = res.data.event.address
- ? res.data.event.address
+ form.controls.title.value = data.event.title;
+ form.controls.location.value = data.event.location;
+ form.controls.address.value = data.event.address
+ ? data.event.address
: "";
- form.controls.start_at.value = new SMDate(res.data.event.start_at, {
+ form.controls.start_at.value = new SMDate(data.event.start_at, {
format: "ymd",
utc: true,
- }).format("yyyy/MM/dd HH:mm:ss");
- form.controls.end_at.value = new SMDate(res.data.event.end_at, {
+ }).format("yyyy/MM/dd HH:mm");
+ form.controls.end_at.value = new SMDate(data.event.end_at, {
format: "ymd",
utc: true,
- }).format("yyyy/MM/dd HH:mm:ss");
- form.controls.status.value = res.data.event.status;
- form.controls.publish_at.value = new SMDate(
- res.data.event.publish_at,
- {
- format: "ymd",
- utc: true,
- }
- ).format("yyyy/MM/dd HH:mm:ss");
+ }).format("yyyy/MM/dd HH:mm");
+ form.controls.status.value = data.event.status;
+ form.controls.publish_at.value = new SMDate(data.event.publish_at, {
+ format: "ymd",
+ utc: true,
+ }).format("yyyy/MM/dd HH:mm");
form.controls.registration_type.value =
- res.data.event.registration_type;
+ data.event.registration_type;
form.controls.registration_data.value =
- res.data.event.registration_data;
- form.controls.content.value = res.data.event.content
- ? res.data.event.content
+ data.event.registration_data;
+ form.controls.content.value = data.event.content
+ ? data.event.content
: "";
- form.controls.hero.value = res.data.event.hero;
+ form.controls.hero.value = data.event.hero;
+ form.controls.price.value = data.event.price;
} catch (err) {
pageError.value = err.response.status;
+ } finally {
+ form.loading(false);
}
}
-
- form.loading(false);
};
const handleSubmit = async () => {
@@ -303,6 +315,7 @@ const handleSubmit = async () => {
registration_data: form.controls.registration_data.value,
content: form.controls.content.value,
hero: form.controls.hero.value,
+ price: form.controls.price.value,
};
if (route.params.id) {