Skip to content

Commit

Permalink
Merge pull request #342 from bcgov/feature/reservationForm
Browse files Browse the repository at this point in the history
Feature/reservation form
  • Loading branch information
loneil authored Dec 8, 2022
2 parents 5fde7f0 + 62c9511 commit af74755
Show file tree
Hide file tree
Showing 9 changed files with 344 additions and 32 deletions.
37 changes: 37 additions & 0 deletions plugins/docker/tenant-proxy.conf.template
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,43 @@ server {
}
}

location ~ ^/multitenancy/reservations$ {
proxy_set_header X-API-KEY ${ACAPY_ADMIN_URL_API_KEY};
proxy_pass ${ACAPY_ADMIN_URL};
allow all;

proxy_set_header Proxy '';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

proxy_hide_header Access-Control-Allow-Origin;
proxy_hide_header Access-Control-Allow-Credentials;

set $CORS_CREDS true;
set $CORS_ORIGIN $http_origin;
set $CORS_PREFLIGHT_CACHE_AGE 600;

if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin $CORS_ORIGIN;
add_header Access-Control-Allow-Methods $http_access_control_request_methods;
add_header Access-Control-Allow-Headers $http_access_control_request_headers;
add_header Access-Control-Allow-Credentials $CORS_CREDS;

add_header Access-Control-Max-Age $CORS_PREFLIGHT_CACHE_AGE;
add_header Content-Type 'text/plain; charset=utf-8';
add_header Content-Length 0;
return 204;
}
if ($request_method != 'OPTIONS') {
add_header Access-Control-Allow-Origin $CORS_ORIGIN;
add_header Access-Control-Allow-Methods $http_access_control_request_methods;
add_header Access-Control-Allow-Headers $http_access_control_request_headers;
add_header Access-Control-Allow-Credentials $CORS_CREDS;
}
}

location ~ ^/status/(live|ready)$ {
proxy_set_header X-API-KEY ${ACAPY_ADMIN_URL_API_KEY};
proxy_pass ${ACAPY_ADMIN_URL};
Expand Down
31 changes: 31 additions & 0 deletions services/tenant-ui/frontend/src/assets/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,34 @@
@import 'tenantuiComponents.scss';
@import 'primevueComponents.scss';
@import 'toast.scss';

// General HTML
html,
body,
#app {
height: 100vh;
padding: 0;
margin: 0;
}

:root {
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
font-size: 14px;
line-height: 24px;
font-weight: 400;

font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}

h1 {
font-size: 3.2em;
line-height: 1.1;
}

a {
color: $tenant-ui-link-color;
}
45 changes: 43 additions & 2 deletions services/tenant-ui/frontend/src/components/Login.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,31 @@
/>
</div>

<div class="py-8">
<!-- Logging In -->
<div v-if="loginMode === LOGIN_MODE.SIGNIN" class="py-6">
<LoginForm />
<div class="mt-6">
<p>
Don't have an account?
<a
href="#"
class="p-button-link login-mode"
@click.prevent="loginMode = LOGIN_MODE.RESERVE"
>Create Request!</a
>
</p>
</div>
</div>

<!-- Making Reservation -->
<div v-else-if="loginMode === LOGIN_MODE.RESERVE" class="py-6">
<Button
label="Go Back to Sign-in"
icon="pi pi-arrow-left"
class="p-button-text"
@click="loginMode = LOGIN_MODE.SIGNIN"
/>
<Reserve />
</div>
</div>
</div>
Expand All @@ -26,10 +49,28 @@
</template>

<script setup lang="ts">
import { storeToRefs } from 'pinia';
// Vue
import { ref } from 'vue';
// PrimeVue
import Button from 'primevue/button';
// Components
import LoginForm from '@/components/LoginForm.vue';
import Reserve from './reservation/Reserve.vue';
// State
import { storeToRefs } from 'pinia';
import { useConfigStore } from '@/store';
const { config } = storeToRefs(useConfigStore());
// Other login form swtiching
enum LOGIN_MODE {
SIGNIN,
RESERVE,
STATUS,
}
const loginMode = ref(LOGIN_MODE.SIGNIN);
const toggleLogin = () => {
alert('hi');
};
</script>

<style scoped lang="scss">
Expand Down
171 changes: 171 additions & 0 deletions services/tenant-ui/frontend/src/components/reservation/Reserve.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
<template>
<form @submit.prevent="handleSubmit(!v$.$invalid)">
<!-- Email -->
<div class="field mt-5 w-full">
<label
for="email"
:class="{ 'p-error': v$.contact_email.$invalid && submitted }"
>Email Address
</label>
<InputText
id="email"
v-model="v$.contact_email.$model"
type="text"
option-label="label"
autocomplete="email"
name="email"
autofocus
class="w-full"
/>
<span v-if="v$.contact_email.$error && submitted">
<span v-for="(error, index) of v$.contact_email.$errors" :key="index">
<small class="p-error block">{{ error.$message }}</small>
</span>
</span>
<small
v-else-if="v$.contact_email.$invalid && submitted"
class="p-error"
>{{ v$.contact_email.required.$message }}</small
>
</div>

<!-- FullName -->
<div class="field mt-5 w-full">
<label
for="full-name"
:class="{ 'p-error': v$.contact_name.$invalid && submitted }"
>Full Name
</label>
<InputText
id="full-name"
v-model="v$.contact_name.$model"
autocomplete="name"
name="fullName"
class="w-full"
/>
<small v-if="v$.contact_name.$invalid && submitted" class="p-error">{{
v$.contact_name.required.$message
}}</small>
</div>

<!-- Phone -->
<div class="field mt-5 w-full">
<label
for="phone"
:class="{ 'p-error': v$.contact_phone.$invalid && submitted }"
>Phone / Mobile
</label>
<InputText
id="phone"
v-model="v$.contact_phone.$model"
autocomplete="phone"
name="phone"
class="w-full"
/>
<small v-if="v$.contact_phone.$invalid && submitted" class="p-error">{{
v$.contact_phone.required.$message
}}</small>
</div>

<!-- Tenant Name -->
<div class="field mt-5 w-full">
<label
for="tenant-name"
:class="{ 'p-error': v$.tenant_name.$invalid && submitted }"
>Tenant Name
</label>
<InputText
id="tenant-name"
v-model="v$.tenant_name.$model"
name="tenant-name"
class="w-full"
/>
<small v-if="v$.tenant_name.$invalid && submitted" class="p-error">{{
v$.tenant_name.required.$message
}}</small>
</div>

<!-- Tenant Reason -->
<div class="field mt-5 w-full">
<label
for="tenant-reason"
:class="{ 'p-error': v$.tenant_reason.$invalid && submitted }"
>Tenant Reason
</label>
<Textarea
id="tenant-reason"
v-model="v$.tenant_reason.$model"
name="tenant-reason"
class="w-full"
:auto-resize="true"
rows="2"
/>
<small v-if="v$.tenant_reason.$invalid && submitted" class="p-error">{{
v$.tenant_reason.required.$message
}}</small>
</div>

<Button
type="submit"
class="w-full mt-5"
label="Request"
:disabled="!!loading"
:loading="!!loading"
/>
</form>
</template>

<script setup lang="ts">
//Vue
import { ref, reactive } from 'vue';
// PrimeVue/Validation/etc
import Button from 'primevue/button';
import InputText from 'primevue/inputtext';
import Textarea from 'primevue/textarea';
import { useToast } from 'vue-toastification';
import { email, required } from '@vuelidate/validators';
import { useVuelidate } from '@vuelidate/core';
// State
import { useReservationStore } from '@/store';
import { storeToRefs } from 'pinia';
const toast = useToast();
// Login Form and validation
const formFields = reactive({
contact_email: '',
contact_name: '',
contact_phone: '',
tenant_name: '',
tenant_reason: '',
});
const rules = {
contact_email: { required, email },
contact_name: { required },
contact_phone: { required },
tenant_name: { required },
tenant_reason: { required },
};
const v$ = useVuelidate(rules, formFields);
// State setup
const reservationStore = useReservationStore();
const { loading, reservation } = storeToRefs(useReservationStore());
// Form submission
const submitted = ref(false);
const handleSubmit = async (isFormValid: boolean) => {
submitted.value = true;
if (!isFormValid) {
return;
}
try {
await reservationStore.makeReservation(formFields);
toast.success(`Your request was received.`);
} catch (err) {
console.error(err);
toast.error(`Failure making request: ${err}`);
}
};
</script>
2 changes: 2 additions & 0 deletions services/tenant-ui/frontend/src/helpers/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,6 @@ export const API_PATH = {
TENANT_SELF: '/tenant/v1/admin/self',
TENANT_MAKE_ISSUER: '/tenant/v1/admin/make-issuer',
TENANT_CONFIGURATION: '/tenant/v1/admin/configuration',

MULTITENANCY_RESERVATION: '/multitenancy/reservations',
};
1 change: 0 additions & 1 deletion services/tenant-ui/frontend/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import router from './router';
import { createApp } from 'vue';
import createI18n from './plugins/i18n/i18n';
import './style.css';
import App from './App.vue';

import { createPinia } from 'pinia';
Expand Down
1 change: 1 addition & 0 deletions services/tenant-ui/frontend/src/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export { useTenantStore } from './tenantStore';
export { useTokenStore } from './tokenStore';
export { useVerifierStore } from './verifierStore';
export { useMessageStore } from './messageStore';
export { useReservationStore } from './reservationStore';

// Innkeeper
export { useInnkeeperTokenStore } from './innkeeper/innkeeperTokenStore';
Expand Down
59 changes: 59 additions & 0 deletions services/tenant-ui/frontend/src/store/reservationStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { API_PATH } from '@/helpers/constants';
import axios from 'axios';
import { defineStore, storeToRefs } from 'pinia';
import { ref } from 'vue';
import { useConfigStore } from './configStore';

export const useReservationStore = defineStore('reservation', () => {
const { config } = storeToRefs(useConfigStore());
// A raw api call without using the interceptors from the acapyApiStore
// Needed for the open call to reservation at this point
const api = axios.create({
baseURL: config.value.frontend.tenantProxyPath,
});

// state
const loading: any = ref(false);
const error: any = ref(null);
const reservation: any = ref(null);

// actions
async function makeReservation(payload: any = {}) {
console.log('> reservationStore.makeReservation');
error.value = null;
loading.value = true;
console.log(payload);
await api
.post(API_PATH.MULTITENANCY_RESERVATION, payload)
.then((res) => {
console.log(res);
reservation.value = res.data.item;
})
.catch((err) => {
error.value = err;
console.log(error.value);
})
.finally(() => {
loading.value = false;
});
console.log('< reservationStore.makeReservation');

if (error.value != null) {
// throw error so $onAction.onError listeners can add their own handler
throw error.value;
}
// return data so $onAction.after listeners can add their own handler
return reservation.value;
}

return {
reservation,
loading,
error,
makeReservation,
};
});

export default {
useReservationStore,
};
Loading

0 comments on commit af74755

Please sign in to comment.