mirror of
https://github.com/lucaspalomodevelop/eventcally.git
synced 2026-03-13 00:07:22 +00:00
441 lines
16 KiB
HTML
441 lines
16 KiB
HTML
{% extends "layout.html" %}
|
||
|
||
{% block header_before_site_js %}
|
||
<link
|
||
type="text/css"
|
||
rel="stylesheet"
|
||
href="https://unpkg.com/bootstrap-vue@2.21.2/dist/bootstrap-vue.min.css"
|
||
/>
|
||
|
||
{% if False | env_override('FLASK_DEBUG') %}
|
||
<script src="https://unpkg.com/vue@2.6.14/dist/vue.js"></script>
|
||
{% else %}
|
||
<script src="https://unpkg.com/vue@2.6.14/dist/vue.min.js"></script>
|
||
{% endif %}
|
||
<script src="https://unpkg.com/bootstrap-vue@2.21.2/dist/bootstrap-vue.min.js"></script>
|
||
<script src="https://unpkg.com/vue-router@2.0.0/dist/vue-router.min.js"></script>
|
||
<script src="https://unpkg.com/vue-i18n@8.25.0/dist/vue-i18n.min.js"></script>
|
||
<script src="https://unpkg.com/axios@0.21.1/dist/axios.min.js"></script>
|
||
<script src="https://unpkg.com/lodash@4.17.21/lodash.min.js"></script>
|
||
<script src="https://unpkg.com/portal-vue@2.1.7/dist/portal-vue.umd.min.js"></script>
|
||
<link
|
||
href="https://unpkg.com/vue-typeahead-bootstrap@2.12.0/dist/VueTypeaheadBootstrap.css"
|
||
rel="stylesheet"
|
||
/>
|
||
<script src="https://unpkg.com/vue-typeahead-bootstrap@2.12.0/dist/VueTypeaheadBootstrap.umd.min.js"></script>
|
||
<script src="https://unpkg.com/vee-validate@3.4.11/dist/vee-validate.full.min.js"></script>
|
||
<script src="{{ url_for('static', filename='vue/common/typeahead.vue.js')}}"></script>
|
||
<script src="{{ url_for('static', filename='vue/common/validated-input.vue.js')}}"></script>
|
||
<script src="{{ url_for('static', filename='vue/common/validated-switch.vue.js')}}"></script>
|
||
<script src="{{ url_for('static', filename='vue/common/validated-textarea.vue.js')}}"></script>
|
||
|
||
{% block component_scripts %}
|
||
{% endblock %}
|
||
{% endblock %}
|
||
|
||
{% block content %}
|
||
{% block vue_container %}
|
||
<div id="vue-container"{% block vue_container_attribs %}{% endblock vue_container_attribs %}><router-view></router-view></div>
|
||
{% endblock %}
|
||
<script>
|
||
Vue.component("vue-typeahead-bootstrap", VueTypeaheadBootstrap);
|
||
Vue.component("ValidationObserver", VeeValidate.ValidationObserver);
|
||
Vue.component("ValidationProvider", VeeValidate.ValidationProvider);
|
||
|
||
Vue.component("custom-typeahead", CustomTypeahead);
|
||
Vue.component("validated-input", ValidatedInput);
|
||
Vue.component("validated-switch", ValidatedSwitch);
|
||
Vue.component("validated-textarea", ValidatedTextarea);
|
||
|
||
{% block component_definitions %}
|
||
{% endblock %}
|
||
|
||
const sharedMessages = {
|
||
en: {
|
||
shared: {
|
||
models: {
|
||
adminUnit: {
|
||
className: "Organization",
|
||
listName: "Organizations",
|
||
name: "Name",
|
||
shortName: "Short name",
|
||
},
|
||
adminUnitRelation: {
|
||
targetOrganization: "Other organization",
|
||
autoVerifyEventReferenceRequests: "Verify reference requests automatically",
|
||
autoVerifyEventReferenceRequestsDescription: "If set, all upcoming reference requests of the other organization are verified automatically.",
|
||
verify: "Verify other organization",
|
||
verifyDescription: "If set, events of the other organization are publicly visible.",
|
||
},
|
||
adminUnitInvitation: {
|
||
email: "Email",
|
||
emailDescription: "The invitation will be sent to this email address.",
|
||
organizationName: "New organization's name",
|
||
relationAutoVerifyEventReferenceRequests: "Verify reference requests automatically",
|
||
relationAutoVerifyEventReferenceRequestsDescription: "If set, all upcoming reference requests of the new organization are verified automatically.",
|
||
relationVerify: "Verify new organization",
|
||
relationVerifyDescription: "If set, events of the new organization are publicly visible.",
|
||
},
|
||
customWidget: {
|
||
className: "Custom widget",
|
||
listName: "Custom widgets",
|
||
widgetType: "Type",
|
||
widgetTypeSearch: "Search",
|
||
widgetTypeCalendar: "Calendar",
|
||
name: "Name",
|
||
},
|
||
event: {
|
||
className: "Event",
|
||
listName: "Events",
|
||
name: "Name",
|
||
},
|
||
eventList: {
|
||
className: "Event list",
|
||
listName: "Event lists",
|
||
name: "Name",
|
||
},
|
||
eventReport: {
|
||
contactName: "Name",
|
||
contactEmail: "Email",
|
||
contactNameDescription: "First and last name",
|
||
message: "Message",
|
||
messageDescription: "Briefly describe in your words why the event is objectionable.",
|
||
},
|
||
},
|
||
cancel: "Cancel",
|
||
decline: "Decline",
|
||
save: "Save",
|
||
submit: "Submit",
|
||
view: "View",
|
||
edit: "Edit",
|
||
delete: "Delete",
|
||
remove: "Remove",
|
||
emptyData: "No data available",
|
||
errors: {
|
||
uniqueViolation:
|
||
"An entry with the entered values already exists. Duplicate entries are not allowed.",
|
||
},
|
||
toast: {
|
||
errorTitle: "Error",
|
||
successTitle: "Success",
|
||
},
|
||
autocomplete: {
|
||
instruction: "Type to search",
|
||
},
|
||
validation: {
|
||
alpha: "The {_field_} field may only contain alphabetic characters",
|
||
alpha_num:
|
||
"The {_field_} field may only contain alpha-numeric characters",
|
||
alpha_dash:
|
||
"The {_field_} field may contain alpha-numeric characters as well as dashes and underscores",
|
||
alpha_spaces:
|
||
"The {_field_} field may only contain alphabetic characters as well as spaces",
|
||
between: "The {_field_} field must be between {min} and {max}",
|
||
confirmed: "The {_field_} field confirmation does not match",
|
||
digits:
|
||
"The {_field_} field must be numeric and exactly contain {length} digits",
|
||
dimensions:
|
||
"The {_field_} field must be {width} pixels by {height} pixels",
|
||
email: "The {_field_} field must be a valid email",
|
||
excluded: "The {_field_} field is not a valid value",
|
||
ext: "The {_field_} field is not a valid file",
|
||
image: "The {_field_} field must be an image",
|
||
integer: "The {_field_} field must be an integer",
|
||
length: "The {_field_} field must be {length} long",
|
||
max_value: "The {_field_} field must be {max} or less",
|
||
max: "The {_field_} field may not be greater than {length} characters",
|
||
mimes: "The {_field_} field must have a valid file type",
|
||
min_value: "The {_field_} field must be {min} or more",
|
||
min: "The {_field_} field must be at least {length} characters",
|
||
numeric: "The {_field_} field may only contain numeric characters",
|
||
oneOf: "The {_field_} field is not a valid value",
|
||
regex: "The {_field_} field format is invalid",
|
||
required_if: "The {_field_} field is required",
|
||
required: "The {_field_} field is required",
|
||
size: "The {_field_} field size must be less than {size}KB",
|
||
double: "The {_field_} field must be a valid decimal",
|
||
uniqueOrganizationName: "Name is already taken",
|
||
},
|
||
},
|
||
},
|
||
de: {
|
||
shared: {
|
||
models: {
|
||
adminUnit: {
|
||
className: "Organisation",
|
||
listName: "Organisationen",
|
||
name: "Name",
|
||
shortName: "Kurzname",
|
||
},
|
||
adminUnitRelation: {
|
||
targetOrganization: "Andere Organisation",
|
||
autoVerifyEventReferenceRequests:
|
||
"Empfehlungsanfragen automatisch verifizieren",
|
||
autoVerifyEventReferenceRequestsDescription: "Wenn gesetzt, werden alle zukünftigen Empfehlungsanfragen der anderen Organisation automatisch verifiziert.",
|
||
verify: "Andere Organisation verifizieren",
|
||
verifyDescription: "Wenn gesetzt, sind Veranstaltungen der anderen Organisation öffentlich sichtbar.",
|
||
},
|
||
adminUnitInvitation: {
|
||
email: "Email",
|
||
emailDescription: "An diese Email-Adresse wird die Einladung gesendet.",
|
||
organizationName: "Name der neuen Organisation",
|
||
relationAutoVerifyEventReferenceRequests: "Empfehlungsanfragen automatisch verifizieren",
|
||
relationAutoVerifyEventReferenceRequestsDescription: "Wenn gesetzt, werden alle zukünftigen Empfehlungsanfragen der neuen Organisation automatisch verifiziert.",
|
||
relationVerify: "Neue Organisation verifizieren",
|
||
relationVerifyDescription: "Wenn gesetzt, sind Veranstaltungen der neuen Organisation öffentlich sichtbar.",
|
||
},
|
||
customWidget: {
|
||
className: "Custom widget",
|
||
listName: "Custom widgets",
|
||
widgetTypeSearch: "Suche",
|
||
widgetTypeCalendar: "Kalender",
|
||
widgetType: "Typ",
|
||
name: "Name",
|
||
},
|
||
event: {
|
||
className: "Veranstaltung",
|
||
listName: "Veranstaltungen",
|
||
name: "Name",
|
||
},
|
||
eventList: {
|
||
className: "Veranstaltungsliste",
|
||
listName: "Veranstaltungslisten",
|
||
name: "Name",
|
||
},
|
||
eventReport: {
|
||
contactName: "Name",
|
||
contactNameDescription: "Vor- und Nachname",
|
||
contactEmail: "Email-Adresse",
|
||
message: "Mitteilung",
|
||
messageDescription: "Beschreibe kurz in deinen Worten, warum die Veranstaltung zu beanstanden ist.",
|
||
},
|
||
},
|
||
cancel: "Abbrechen",
|
||
decline: "Ablehnen",
|
||
save: "Speichern",
|
||
submit: "Senden",
|
||
view: "Anzeigen",
|
||
edit: "Bearbeiten",
|
||
delete: "Löschen",
|
||
remove: "Entfernen",
|
||
emptyData: "Keine Daten vorhanden",
|
||
errors: {
|
||
uniqueViolation:
|
||
"Ein Eintrag mit den eingegebenen Werten existiert bereits. Doppelte Einträge sind nicht erlaubt.",
|
||
},
|
||
toast: {
|
||
errorTitle: "Fehler",
|
||
successTitle: "Erfolg",
|
||
},
|
||
autocomplete: {
|
||
instruction: "Tippen um zu suchen",
|
||
},
|
||
validation: {
|
||
alpha: "{_field_} darf nur alphabetische Zeichen enthalten",
|
||
alpha_dash:
|
||
"{_field_} darf alphanumerische Zeichen sowie Striche und Unterstriche enthalten",
|
||
alpha_num: "{_field_} darf nur alphanumerische Zeichen enthalten",
|
||
alpha_spaces:
|
||
"{_field_} darf nur alphanumerische Zeichen und Leerzeichen enthalten",
|
||
between: "{_field_} muss zwischen {min} und {max} liegen",
|
||
confirmed: "Die Bestätigung von {_field_} stimmt nicht überein",
|
||
digits:
|
||
"{_field_} muss numerisch sein und exakt {length} Ziffern enthalten",
|
||
dimensions: "{_field_} muss {width} x {height} Bildpunkte groß sein",
|
||
email: "{_field_} muss eine gültige E-Mail-Adresse sein",
|
||
excluded: "{_field_} muss ein gültiger Wert sein",
|
||
ext: "{_field_} muss eine gültige Datei sein",
|
||
image: "{_field_} muss eine Grafik sein",
|
||
oneOf: "{_field_} muss ein gültiger Wert sein",
|
||
integer: "{_field_} muss eine ganze Zahl sein",
|
||
length: "Die Länge von {_field_} muss {length} sein",
|
||
max: "{_field_} darf nicht länger als {length} Zeichen sein",
|
||
max_value: "{_field_} darf maximal {max} sein",
|
||
mimes: "{_field_} muss einen gültigen Dateityp haben",
|
||
min: "{_field_} muss mindestens {length} Zeichen lang sein",
|
||
min_value: "{_field_} muss mindestens {min} sein",
|
||
numeric: "{_field_} darf nur numerische Zeichen enthalten",
|
||
regex: "Das Format von {_field_} ist ungültig",
|
||
required: "{_field_} ist ein Pflichtfeld",
|
||
required_if: "{_field_} ist ein Pflichtfeld",
|
||
size: "{_field_} muss kleiner als {size}KB sein",
|
||
double: "Das Feld {_field_} muss eine gültige Dezimalzahl sein",
|
||
uniqueOrganizationName: "Der Name ist bereits vergeben",
|
||
},
|
||
},
|
||
},
|
||
};
|
||
const i18n = new VueI18n({
|
||
locale: "de",
|
||
messages: sharedMessages,
|
||
silentFallbackWarn: true,
|
||
});
|
||
|
||
VeeValidate.configure({
|
||
defaultMessage: (field, values) => {
|
||
return i18n.t(`shared.validation.${values._rule_}`, values);
|
||
}
|
||
});
|
||
|
||
Object.keys(VeeValidate.Rules).forEach((rule) => {
|
||
VeeValidate.extend(rule, VeeValidate.Rules[rule]);
|
||
});
|
||
|
||
VeeValidate.extend('uniqueOrganizationName', {
|
||
validate: async value => {
|
||
try {
|
||
const response = await axios.get(
|
||
`/api/v1/organizations?keyword=${value}`, {
|
||
withCredentials: true,
|
||
}
|
||
);
|
||
return !response.data.items.some(o => o.name == value);
|
||
} catch (err) {
|
||
return true;
|
||
}
|
||
}
|
||
}, {
|
||
immediate: false
|
||
});
|
||
|
||
{% block vue_routes %}
|
||
const routes = [];
|
||
{% endblock %}
|
||
|
||
const router = new VueRouter({
|
||
routes: routes,
|
||
mode: "history",
|
||
base: "/",
|
||
});
|
||
|
||
axios.defaults.baseURL = "{{ get_base_url() }}";
|
||
axios.interceptors.request.use(
|
||
function (config) {
|
||
if (config) {
|
||
this.app.handleAxiosStart(config);
|
||
}
|
||
return config;
|
||
},
|
||
function (error) {
|
||
this.app.handleAxiosError(error);
|
||
return Promise.reject(error);
|
||
}
|
||
);
|
||
axios.interceptors.response.use(
|
||
function (response) {
|
||
if (response && response.config) {
|
||
this.app.handleAxiosFinish(response.config);
|
||
}
|
||
return response;
|
||
},
|
||
function (error) {
|
||
this.app.handleAxiosError(error);
|
||
return Promise.reject(error);
|
||
}
|
||
);
|
||
|
||
{% block vue_app_data %}
|
||
var vue_app_data = {};
|
||
{% endblock %}
|
||
|
||
var app = new Vue({
|
||
el: "#vue-container",
|
||
i18n,
|
||
router: router,
|
||
data: vue_app_data,
|
||
methods: {
|
||
handleAxiosStart(config) {
|
||
if (
|
||
config &&
|
||
config.handler &&
|
||
config.handler.hasOwnProperty("handleRequestStart")
|
||
) {
|
||
config.handler.handleRequestStart();
|
||
}
|
||
|
||
if (
|
||
config &&
|
||
config.hasOwnProperty("handleLoading")
|
||
) {
|
||
config.handleLoading(true);
|
||
}
|
||
},
|
||
handleAxiosFinish(config) {
|
||
if (
|
||
config &&
|
||
config.handler &&
|
||
config.handler.hasOwnProperty("handleRequestFinish")
|
||
) {
|
||
config.handler.handleRequestFinish();
|
||
}
|
||
|
||
if (
|
||
config &&
|
||
config.hasOwnProperty("handleLoading")
|
||
) {
|
||
config.handleLoading(false);
|
||
}
|
||
},
|
||
handleAxiosError(error) {
|
||
if (error && error.config) {
|
||
this.handleAxiosFinish(error.config);
|
||
}
|
||
|
||
const status = error && error.response && error.response.status;
|
||
let message = error.message || error;
|
||
|
||
if (status == 400) {
|
||
message =
|
||
(error &&
|
||
error.response &&
|
||
error.response.data &&
|
||
error.response.data.message) ||
|
||
error;
|
||
errorName =
|
||
error &&
|
||
error.response &&
|
||
error.response.data &&
|
||
error.response.data.name;
|
||
|
||
if (errorName == "Unique Violation") {
|
||
message = this.$t("shared.errors.uniqueViolation");
|
||
}
|
||
}
|
||
|
||
if (
|
||
error.config &&
|
||
error.config.handler &&
|
||
error.config.handler.hasOwnProperty("handleRequestError")
|
||
) {
|
||
error.config.handler.handleRequestError(error, message);
|
||
} else {
|
||
this.makeErrorToast(message);
|
||
}
|
||
},
|
||
makeErrorToast(message) {
|
||
this.makeToast(message, "danger", this.$t("shared.toast.errorTitle"));
|
||
},
|
||
makeSuccessToast(message) {
|
||
this.makeToast(
|
||
message,
|
||
"success",
|
||
this.$t("shared.toast.successTitle")
|
||
);
|
||
},
|
||
makeToast(message, variant, title) {
|
||
this.$bvToast.toast(message, {
|
||
title: title,
|
||
variant: variant,
|
||
toaster: "b-toaster-top-center",
|
||
noCloseButton: true,
|
||
solid: true,
|
||
});
|
||
},
|
||
goBack(fallbackPath) {
|
||
window.history.length > 1 ? this.$router.go(-1) : this.$router.push({ path: fallbackPath })
|
||
},
|
||
},
|
||
});
|
||
</script>
|
||
{% endblock %}
|