diff --git a/.github/workflows/cd-portal-build-dev.yaml b/.github/workflows/cd-portal-build-dev.yaml new file mode 100644 index 0000000000..7a4deb4cd4 --- /dev/null +++ b/.github/workflows/cd-portal-build-dev.yaml @@ -0,0 +1,85 @@ +name: Portal & API - Development build +on: + workflow_dispatch: +defaults: + run: + working-directory: "." + +jobs: + + openshift-compile-client-app: + name: Compile Client App + # needs: [] + runs-on: ubuntu-latest + steps: + - name: OpenShift Build + env: + BUILD_NAME: cllc-public-angular + NAMESPACE: ${{ secrets.OpenShiftBuildNamespace }} + uses: redhat-developer/openshift-actions@v1.1 + with: + version: "latest" + openshift_server_url: ${{ secrets.OpenShiftServerURL }} + parameters: '{"apitoken": "${{ secrets.OpenShiftToken }}", "acceptUntrustedCerts": "true"}' + cmd: | + 'version' + 'project ${NAMESPACE}' + 'start-build ${BUILD_NAME} -n ${NAMESPACE} -w --follow' + + openshift-build-client-image: + name: Build Client Nginx Image + needs: openshift-compile-client-app + runs-on: ubuntu-latest + steps: + - name: OpenShift Build + env: + BUILD_NAME: cllc-public-frontend + NAMESPACE: ${{ secrets.OpenShiftBuildNamespace }} + uses: redhat-developer/openshift-actions@v1.1 + with: + version: "latest" + openshift_server_url: ${{ secrets.OpenShiftServerURL }} + parameters: '{"apitoken": "${{ secrets.OpenShiftToken }}", "acceptUntrustedCerts": "true"}' + cmd: | + 'version' + 'project ${NAMESPACE}' + 'start-build ${BUILD_NAME} -n ${NAMESPACE} -w --follow' + + openshift-build-client-api: + name: Build Client API + # needs: [] + runs-on: ubuntu-latest + steps: + - name: OpenShift Build + env: + BUILD_NAME: cllc-public-api + NAMESPACE: ${{ secrets.OpenShiftBuildNamespace }} + uses: redhat-developer/openshift-actions@v1.1 + with: + version: "latest" + openshift_server_url: ${{ secrets.OpenShiftServerURL }} + parameters: '{"apitoken": "${{ secrets.OpenShiftToken }}", "acceptUntrustedCerts": "true"}' + cmd: | + 'version' + 'project ${NAMESPACE}' + 'start-build ${BUILD_NAME} -n ${NAMESPACE} -w --follow' + + openshift-tag-images: + name: Tag Images + needs: [openshift-build-client-image, openshift-build-client-api] + runs-on: ubuntu-latest + steps: + - name: OpenShift Tag + env: + BUILD_NAME: cllc-public-api + NAMESPACE: ${{ secrets.OpenShiftBuildNamespace }} + uses: redhat-developer/openshift-actions@v1.1 + with: + version: "latest" + openshift_server_url: ${{ secrets.OpenShiftServerURL }} + parameters: '{"apitoken": "${{ secrets.OpenShiftToken }}", "acceptUntrustedCerts": "true"}' + cmd: | + 'version' + 'project ${NAMESPACE}' + 'tag -n ${NAMESPACE} cllc-public-api:latest cllc-public-api:dev' + 'tag -n ${NAMESPACE} cllc-public-frontend:latest cllc-public-frontend:dev' diff --git a/.gitignore b/.gitignore index 2f2f20b610..ae80bfedf8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ .env .DS_Store - +secrets.json /cllc-interfaces-test/SharePoint/Properties/launchSettings.json cllc-public-app/ClientApp/e2e-reports /functional-tests/bin diff --git a/cllc-public-app/ClientApp/package.json b/cllc-public-app/ClientApp/package.json index 75a7d3e7f1..713ef07d5c 100644 --- a/cllc-public-app/ClientApp/package.json +++ b/cllc-public-app/ClientApp/package.json @@ -46,6 +46,7 @@ "leaflet": "^1.7.1", "lodash": "^4.17.21", "material-design-icons": "^3.0.1", + "moment-timezone": "^0.5.45", "mygovbc-bootstrap-theme": "^0.4.1", "ng-busy": "^12.0.2", "ng2-currency-mask": "^12.0.3", diff --git a/cllc-public-app/ClientApp/src/app/components/police-representative/police-summary/police-summary.component.ts b/cllc-public-app/ClientApp/src/app/components/police-representative/police-summary/police-summary.component.ts index 4f1ea13ce2..5bf29cb56e 100644 --- a/cllc-public-app/ClientApp/src/app/components/police-representative/police-summary/police-summary.component.ts +++ b/cllc-public-app/ClientApp/src/app/components/police-representative/police-summary/police-summary.component.ts @@ -229,7 +229,7 @@ export class PoliceSummaryComponent extends FormBase implements OnInit { if (loc.eventDates?.length > 0) { const formatterdDates = []; loc.eventDates.forEach(ed => { - ed = Object.assign(new SepSchedule(null), ed); + ed = Object.assign(new SepSchedule(null, null), ed); formatterdDates.push({ ed, ...ed.toEventFormValue() }); }); loc.eventDates = formatterdDates; diff --git a/cllc-public-app/ClientApp/src/app/components/sep/sep-application/event/event.component.html b/cllc-public-app/ClientApp/src/app/components/sep/sep-application/event/event.component.html index f5d2c0297a..6470eab6bd 100644 --- a/cllc-public-app/ClientApp/src/app/components/sep/sep-application/event/event.component.html +++ b/cllc-public-app/ClientApp/src/app/components/sep/sep-application/event/event.component.html @@ -314,6 +314,22 @@

Event Date(s)

+ + +
+ + +
+
+ + +
+
+ @@ -323,8 +339,8 @@

Event Date(s)

@@ -334,8 +350,8 @@

Event Date(s)

[valid]="!item.get('serviceEndValue').touched || item.get('serviceEndValue').valid"> @@ -346,12 +362,11 @@

Event Date(s)

- - +

- If the area you’re applying from or if the event is located outside of PST, please note the time on your permit may not accurately reflect the time your event is scheduled to start. Please contact the LCRB team at LCRB.SEP@gov.bc.ca + Please note that the time on the license you receive will be in America / Vancouver time zone. For more information please contact the LCRB team at LCRB.SEP@gov.bc.ca

-
+
diff --git a/cllc-public-app/ClientApp/src/app/components/sep/sep-application/event/event.component.ts b/cllc-public-app/ClientApp/src/app/components/sep/sep-application/event/event.component.ts index e8f3aaab79..fbaa496eb2 100644 --- a/cllc-public-app/ClientApp/src/app/components/sep/sep-application/event/event.component.ts +++ b/cllc-public-app/ClientApp/src/app/components/sep/sep-application/event/event.component.ts @@ -35,7 +35,7 @@ export class EventComponent extends FormBase implements OnInit { validationMessages: string[]; previewCities: AutoCompleteItem[] = []; autocompleteCities: AutoCompleteItem[] = []; - isPacificTimeZone: boolean; + isPacificTimeZone: boolean = true; isOpen: boolean[][] = []; get minDate() { return new Date(); @@ -86,7 +86,7 @@ export class EventComponent extends FormBase implements OnInit { } ngOnInit(): void { - this.isPacificTimeZone = true; + // create a form for the basic details this.form = this.fb.group({ sepCity: ["", [Validators.required, Validators.minLength(2)]], @@ -122,9 +122,7 @@ export class EventComponent extends FormBase implements OnInit { } }); } - checkTimeZone() { - this.isPacificTimeZone = !this.isPacificTimeZone; - } + setFormValue(app: SepApplication) { // if there's an app if (app) { @@ -272,10 +270,11 @@ export class EventComponent extends FormBase implements OnInit { serviceStartValue: ["9:00 AM", [Validators.required]], serviceEndValue: ["9:30 PM", [Validators.required]], liquorServiceHoursExtensionReason: [""], - disturbancePreventionMeasuresDetails: [""] + disturbancePreventionMeasuresDetails: [""], + isPacificTimeZone: [this.isPacificTimeZone] }, { validators: eventTimesValidator }); - eventDate = Object.assign(new SepSchedule(null), eventDate); + eventDate = Object.assign(new SepSchedule(null, this.isPacificTimeZone), eventDate); const val = eventDate.toEventFormValue(); // Set default to event start date @@ -283,9 +282,14 @@ export class EventComponent extends FormBase implements OnInit { val.eventDate = this?.sepApplication?.eventStartDate; } datesForm.patchValue(val); + + datesForm.get('isPacificTimeZone').valueChanges.subscribe(value => { + this.isPacificTimeZone = value; + }); + return datesForm; } - + getIsOpen(locationIndex: number, eventIndex: number): boolean { if(this.isOpen[locationIndex] === undefined) { return true; @@ -450,7 +454,7 @@ export class EventComponent extends FormBase implements OnInit { formData?.eventLocations.forEach(location => { const dateValues = []; location?.eventDates.forEach(sched => { - dateValues.push(new SepSchedule(sched)); + dateValues.push(new SepSchedule(sched, this.isPacificTimeZone)); }); location.eventDates = dateValues; }); @@ -506,5 +510,4 @@ export class EventComponent extends FormBase implements OnInit { this.showValidationMessages = true; } } - } diff --git a/cllc-public-app/ClientApp/src/app/components/sep/sep-application/summary/summary.component.ts b/cllc-public-app/ClientApp/src/app/components/sep/sep-application/summary/summary.component.ts index 66dce2b929..36cc301975 100644 --- a/cllc-public-app/ClientApp/src/app/components/sep/sep-application/summary/summary.component.ts +++ b/cllc-public-app/ClientApp/src/app/components/sep/sep-application/summary/summary.component.ts @@ -244,7 +244,7 @@ export class SummaryComponent implements OnInit { if (loc.eventDates?.length > 0) { const formatterdDates = []; loc.eventDates.forEach(ed => { - ed = Object.assign(new SepSchedule(null), ed); + ed = Object.assign(new SepSchedule(null, null), ed); formatterdDates.push({ ed, ...ed.toEventFormValue() }); }); loc.eventDates = formatterdDates; diff --git a/cllc-public-app/ClientApp/src/app/models/sep-schedule.model.ts b/cllc-public-app/ClientApp/src/app/models/sep-schedule.model.ts index 6d16bc3c1c..8b368361b4 100644 --- a/cllc-public-app/ClientApp/src/app/models/sep-schedule.model.ts +++ b/cllc-public-app/ClientApp/src/app/models/sep-schedule.model.ts @@ -1,4 +1,5 @@ import { format, addDays } from "date-fns"; +import * as moment from 'moment-timezone'; export class SepSchedule { id: string; // server side primary key @@ -10,80 +11,103 @@ export class SepSchedule { serviceEnd: Date; liquorServiceHoursExtensionReason: string; disturbancePreventionMeasuresDetails: string; + readonly vancouverTimeZone: string = "America/Vancouver"; + readonly edmontonTimeZone: string = "America/Edmonton"; - constructor(sched: IEventFormValue) { + + constructor(sched: IEventFormValue, isPacificTimeZone: boolean) { + const timeZone = isPacificTimeZone ? this.vancouverTimeZone : this.edmontonTimeZone; if (sched) { this.id = sched.id; - this.eventStart = this.formatDate(sched.eventDate, sched.eventStartValue); - this.eventEnd = this.formatDate(sched.eventDate, sched.eventEndValue); - this.serviceStart = this.formatDate(sched.eventDate, sched.serviceStartValue); - this.serviceEnd = this.formatDate(sched.eventDate, sched.serviceEndValue); + this.eventStart = this.formatDate(sched.eventDate, sched.eventStartValue, isPacificTimeZone); + this.eventEnd = this.formatDate(sched.eventDate, sched.eventEndValue, isPacificTimeZone); + this.serviceStart = this.formatDate(sched.eventDate, sched.serviceStartValue, isPacificTimeZone); + this.serviceEnd = this.formatDate(sched.eventDate, sched.serviceEndValue, isPacificTimeZone); this.liquorServiceHoursExtensionReason = sched.liquorServiceHoursExtensionReason; this.disturbancePreventionMeasuresDetails = sched.disturbancePreventionMeasuresDetails; } } + // Convert the SepSchedule object to an IEventFormValue object toEventFormValue(): IEventFormValue { const result = { id: this.id } as IEventFormValue; result.eventDate = this.eventStart; - - if (this.eventStart) { result.eventStartValue = format(new Date(this.eventStart), "h:mm aa"); } + if (this.eventEnd) { result.eventEndValue = format(new Date(this.eventEnd), "h:mm aa"); } + if (this.serviceStart) { result.serviceStartValue = format(new Date(this.serviceStart), "h:mm aa"); } + if (this.serviceEnd) { result.serviceEndValue = format(new Date(this.serviceEnd), "h:mm aa"); } + result.liquorServiceHoursExtensionReason = this.liquorServiceHoursExtensionReason; result.disturbancePreventionMeasuresDetails = this.disturbancePreventionMeasuresDetails; return result; } + // Get the number of service hours getServiceHours(): number { const serviceHours = parseInt(format(new Date(this.serviceEnd), "H")) - parseInt(format(new Date(this.serviceStart), "H")); return serviceHours; } + // Check if the time is after 12:00 AM + private isNextDay(time: string): boolean { + const dayBreakIndex = TIME_SLOTS.indexOf(TIME_SLOTS.find(slot => slot.dayBreak === true)); + const timeIndex = TIME_SLOTS.indexOf(TIME_SLOTS.find(slot => slot.value === time)); + return timeIndex >= dayBreakIndex; + } - /** - * - * @param eventDate - * @param time, assumed format "HH:MM [AM,PM]" e.g. '6:30 PM' - */ - private formatDate(eventDate: Date, time: string): Date { - + // Convert the time to 24-hour format and convert the date to Pacific Time + private formatDate(eventDate: Date, time: string, isPacificTimeZone: boolean): Date { let tempDate = new Date(eventDate); - - // let day = parseInt(format(new Date(eventDate), "dd"), 10); - - // console.log("formatting date: ", eventDate, time) + if (this.isNextDay(time)) { - // console.log("is next day") tempDate = addDays(tempDate, 1); - // day += 1; } - // const dateString = `${day} ${format(tempDate, "d MMM yyyy")} ${time}`; - const dateString = `${format(tempDate, "d MMM yyyy")} ${time}`; - // console.log("dateString:",dateString); - const result = new Date(dateString); - // console.log("result is: ", result); - return result; + + // Convert time to 24-hour format + const time24 = convertTo24Hour(time); + + // Create a date string in the format "yyyy-MM-ddTHH:mm:ss" + const dateString = `${format(tempDate, "yyyy-MM-dd")}T${time24}`; + + // Convert the date string to Pacific Time + let dateInPacific = moment.tz(dateString, "America/Los_Angeles"); + + // Convert to UTC, subtract an hour if isPacificTimeZone is false + if (!isPacificTimeZone) { + dateInPacific = dateInPacific.clone().tz("UTC").add(-1, 'hours'); + } else { + dateInPacific = dateInPacific.clone().tz("UTC"); + } + + return dateInPacific.toDate(); } +} - private isNextDay(time: string): boolean { - const dayBreakIndex = TIME_SLOTS.indexOf(TIME_SLOTS.find(slot => slot.dayBreak === true)); - const timeIndex = TIME_SLOTS.indexOf(TIME_SLOTS.find(slot => slot.value === time)); - return timeIndex >= dayBreakIndex; +// Convert the time to 24-hour format +function convertTo24Hour(time) { + const [main, period] = time.split(' '); + let [hours, minutes] = main.split(':'); + + if (period === 'PM' && hours !== '12') { + hours = (Number(hours) + 12).toString(); + } else if (period === 'AM' && hours === '12') { + hours = '00'; } + return `${hours.padStart(2, '0')}:${minutes}`; }