@@ -159,67 +159,36 @@ const hideNestedDropdown = (index) => {
diff --git a/pages/Page/[id].vue b/pages/Page/[id].vue
index 9903a0a9..1abfb3fb 100644
--- a/pages/Page/[id].vue
+++ b/pages/Page/[id].vue
@@ -293,6 +293,9 @@ setImageAutoSlide()
.reply-box(v-if="reply.reply.length > 0" style="padding: 1rem; text-align: left; border-bottom: 1px solid black")
.reply-header(style="font-size: 1rem; font-weight: bold; margin-bottom: 2.5rem; margin-left: 1rem") {{reply.name}}
.reply-body(style="font-size: 1rem; color: #666; margin-bottom: 2.5rem;") {{reply.reply}}
+div.flex(style="color:gray; font-weight: 700; justify-content:center; align-items: center; height: 100px;")
+ .col-md-8.mx-9(class="sm:col-span-1 sm:mr-11")
+ .div.px-8.py-4(style="color: #6E6E6E; font-weight: 500; font-size: 18px; line-height: 28px; letter-spacing: -0.078px; word-break: break-word;" id="obituary") {{ pageDataDB.obituary }}
div.flex(style="color:gray; font-weight: 700; justify-content:center; align-items: center; height: 100px;")
label SHARE THIS PAGE |
.col
@@ -304,8 +307,4 @@ div.flex(style="color:gray; font-weight: 700; justify-content:center; align-item
.col
button(@click="shareMail")
img(src="/mail_fa.png" style="width:50px; height:29px;")
- .col
- p {{ "" }}
- .col-md-8.mx-9(class="sm:col-span-1 sm:mr-11")
- .div.px-8.py-4(style="color: #6E6E6E; font-weight: 500; font-size: 18px; line-height: 28px; letter-spacing: -0.078px; word-break: break-word;" id="obituary") {{ pageDataDB.obituary }}
diff --git a/server/api/family.post.ts b/server/api/family.post.ts
index 962ab5ed..0540d845 100644
--- a/server/api/family.post.ts
+++ b/server/api/family.post.ts
@@ -42,10 +42,9 @@ const body = await readBody(event);
middle_name,
last_name,
phone, address } = body
-if(event.context.user?.user_role == "advocate" || event.context.user.user_role === "admin") {
+if(event.context.user?.user_role === "advocate" || event.context.user.user_role === "admin") {
try {
- await sendEmail(body.email, "invitation", "Invitation to Carson's village", ({...body, url: `${runtime.BASEURL}api/login`}))
- const queryRes = await prisma.family.create({
+ const queryRes = await event.context.client.family.create({
data: {
family_name: family_name,
AdvocateResponsible: {
@@ -67,6 +66,7 @@ if(event.context.user?.user_role == "advocate" || event.context.user.user_role =
}
}
)
+ await sendEmail(body.email, "invitation", "Invitation to Carson's village", ({...body, url: `${runtime.BASEURL}api/login`}))
return queryRes
} catch (e) {
console.log(e)
diff --git a/server/api/user.post.ts b/server/api/user.post.ts
index 62ffcd78..6446f1b6 100644
--- a/server/api/user.post.ts
+++ b/server/api/user.post.ts
@@ -50,30 +50,36 @@ if(event.context.user?.user_role === "advocate" || event.context.user.user_role
}
});
await sendEmail(body.email, "invitation", "Invitation to Carson's village", ({...body, url: `${runtime.BASEURL}api/login`}))
+ return { success: true, result: queryRes }
} else if(body.user_role == "family") {
- const pages = body.Pages
delete body.Pages
+ delete body.AdvocateFamily
const userRes = await prisma.user.create({
data: {
...body, cuid: undefined,
- }})
+ }})
const queryRes = await prisma.family.update({
where: { cuid: body.familyCuid },
data: {
- Pages: pages, updated_at: now,
+ updated_at: now,
FamilyMembers: {
connect: {
cuid: userRes.cuid
}
},
}
+ }
+ )
+ await sendEmail(body.email, "invitation", "Invitation to Carson's village", ({...body, url: `${runtime.BASEURL}api/login`}))
+ return { success: true, result: queryRes }
}
- )}
- return true
- } catch(e){
- console.error(e);
- return false
+ } catch(e: any){
+ let error = e as string || undefined
+ throw createError({
+ statusCode: 500,
+ message: e.message as unknown as string,
+ })
}
} else{
return await sendRedirect(event, loginRedirectUrl());
diff --git a/server/api/user.put.ts b/server/api/user.put.ts
index 100b5618..b1b32c25 100644
--- a/server/api/user.put.ts
+++ b/server/api/user.put.ts
@@ -53,11 +53,14 @@ if(event.context.user?.user_role == "advocate" || event.context.user.user_role
}
})
}
- return true;
- } catch(e) {
- console.log(e)
- return false
- }
+ return { success: true, result: "success" }
+ } catch(e: any) {
+ console.error(e);
+ throw createError({
+ statusCode: 500,
+ message: e.message as unknown as string,
+ })
+ }
} else {
return await sendRedirect(event, loginRedirectUrl());
}
From 1957f4d5b00413a67f0daafd0f91a57912429f59 Mon Sep 17 00:00:00 2001
From: Isi Emordi
Date: Mon, 15 Jul 2024 13:37:09 -0500
Subject: [PATCH 09/47] started apis for email list
---
server/api/constant_contacts.ts | 15 +++++++++++++++
1 file changed, 15 insertions(+)
create mode 100644 server/api/constant_contacts.ts
diff --git a/server/api/constant_contacts.ts b/server/api/constant_contacts.ts
new file mode 100644
index 00000000..caa8b17e
--- /dev/null
+++ b/server/api/constant_contacts.ts
@@ -0,0 +1,15 @@
+import {nanoid} from "nanoid"
+
+const state = {};
+const genState = () => { const s = nanoid(); state[s] = 1; return s}
+const runtime = useRuntimeConfig()
+
+export const authRequestUrl = () => `https://authz.constantcontact.com/oauth2/default/v1/authorize?client_id=${runtime.CONSTANT_CONTACT_CLIENTID}&redirect_uri=${runtime.BASE_URL}&response_type=code&scope=account_read%20account_update%20contact_data%20offline_access&state=${body.state}&nonce=${genState()}`
+
+export const verifyNonce = (nonce: string) => {
+ if (state[nonce]) {
+ delete state[nonce]
+ return true
+ }
+ return false
+ }
\ No newline at end of file
From 32d7496b606b289e27ab64da6520a53fdd1661ba Mon Sep 17 00:00:00 2001
From: Alonso Toji
Date: Mon, 15 Jul 2024 15:35:19 -0500
Subject: [PATCH 10/47] edit page view to look cleaner and look more like what
Jason wants
---
pages/EditPage/[EditPageId].vue | 15 +++
pages/Page/[id].vue | 101 ++++++++++++------
.../migration.sql | 3 +
.../migration.sql | 2 +
prisma/schema.prisma | 3 +
types.d.ts | 3 +
utils.ts | 23 +++-
7 files changed, 114 insertions(+), 36 deletions(-)
create mode 100644 prisma/migrations/20240715194925_service_location_names/migration.sql
create mode 100644 prisma/migrations/20240715202032_donation_description/migration.sql
diff --git a/pages/EditPage/[EditPageId].vue b/pages/EditPage/[EditPageId].vue
index 902e4a44..7c77aed7 100644
--- a/pages/EditPage/[EditPageId].vue
+++ b/pages/EditPage/[EditPageId].vue
@@ -43,13 +43,16 @@ const data = ref({
day_of_passing: null,
visitation_date: null,
visitation_location: "",
+ visitation_address: "",
visitation_description: "",
funeral_date: null,
funeral_description: "",
funeral_location: "",
+ funeral_address: "",
obituary: "",
deadline: null,
donation_goal: 0,
+ donation_description: "",
amount_raised: 0,
amount_distributed: 0,
profileImageCuid: "",
@@ -350,6 +353,10 @@ description="Here, you select from photos you uploaded to show up first on the F
CVLabel Location
.col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
CVInput(v-model='data.visitation_location' placeholder="optional")
+ .py-4.grid(class="sm:grid-cols-3")
+ CVLabel Address
+ .col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
+ CVInput(v-model='data.visitation_address' placeholder="optional")
.py-4.grid(class="sm:grid-cols-3")
CVLabel Description
.col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
@@ -365,6 +372,10 @@ description="Here, you select from photos you uploaded to show up first on the F
CVLabel Location
.col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
CVInput(v-model='data.funeral_location' placeholder="optional")
+ .py-4.grid(class="sm:grid-cols-3")
+ CVLabel Address
+ .col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
+ CVInput(v-model='data.funeral_address' placeholder="optional")
.py-4.grid(class="sm:grid-cols-3")
CVLabel Description
.col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
@@ -386,6 +397,10 @@ description="Here, you select from photos you uploaded to show up first on the F
CVLabel Deadline Date
.col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
CVDatepicker(v-model='data.deadline')
+ .py-4.grid(class="sm:grid-cols-3")
+ CVLabel Description
+ .col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
+ CVTextArea(v-model='data.donation_description' placeholder="optional")
.information.rounded-md.mx-9.my-2.text-center(class="sm:text-start text-white bg-blue-999")
CVLegend Comment Moderation
.py-4.grid.flex-box.flex-row.item-centered.gap-1(v-if="replies?.length" style="line-height: 0px;")
diff --git a/pages/Page/[id].vue b/pages/Page/[id].vue
index 9903a0a9..473285c0 100644
--- a/pages/Page/[id].vue
+++ b/pages/Page/[id].vue
@@ -11,7 +11,7 @@
import type { User, Page, PageDonation, Image, Reply, Family} from '@/types.d.ts'
-import { dateFormat, donationFormat } from '@/utils'
+import { dateFormat, donationFormat, longDateFormat } from '@/utils'
import CVReplySystem from '@/components/CVReplySystem.vue'
import CVReply from '@/components/CVReply.vue'
@@ -27,10 +27,12 @@ const pageData = ref({
visitation_date: null,
visitation_location: "",
+ visitation_address: "",
visitation_description: "",
funeral_date: "",
funeral_description: "",
funeral_location: "",
+ funeral_address: "",
obituary: "",
deadline: "",
donation_goal: 0,
@@ -239,46 +241,75 @@ setImageAutoSlide()
button.absolute.right-8.top-64.bg-black.text-white(@click="nextImage" style="opacity:0.7; --tw-text-opacity: 1; width: 46px; height: 46px; border-radius:50%; align-items: center; justify-content: center; line-height: 2; text-align: center;color: white;") >
img.w-96(style="z-index: -1; object-fit:cover;" :src="imageData[currentImage].url")
// services list
- .py-4.flex.flex-col.gap-5(style="font-size: 18px")
- .text-gray-dark.font-poppins.text-2xl.text-left.font-bold(style="line-height: 36px; text-shadow: 3px 3px 4px rgba(0, 0, 0, 0.25);") Services
- .flex.flex-col.gap-5(class="lg:grid lg:grid-cols-2")
- .flex.flex-col.gap-5(v-if="pageDataDB.visitation_date")
+ .py-4.flex.flex-col(class="lg:grid lg:grid-rows-6")
+ .flex.flex-col(v-if="pageDataDB.visitation_date")
.text-gray-dark.font-poppins.text-2xl.text-left.font-bold(style="line-height: 36px; text-shadow: 3px 3px 4px rgba(0, 0, 0, 0.25);") Visitation
- .flex.gap-5
- .font-outfit {{ "Date:" }}
- .font-outfit {{ dateFormat(pageDataDB.visitation_date, true) }}
- .flex.gap-5
- .font-outfit {{ "Location:" }}
+ .flex.row.gap-2
+ .font-outfit.flex-col.font-bold {{ "Date:" }}
+ .font-outfit {{ longDateFormat(pageDataDB.visitation_date) }}
+ .flex.row.gap-2
+ .font-outfit.font-bold {{ "Time:" }}
+ .font-outfit.gap-y-5 {{ longDateFormat(pageDataDB.visitation_date, true) }}
+ .flex.row.gap-2
+ .font-outfit.font-bold {{ "Location:" }}
.font-outfit.whitespace-normal {{ pageDataDB.visitation_location ? pageDataDB.visitation_location : "TBD" }}
- .font-outfit {{ pageDataDB.visitation_description }}
- .flex.flex-col.gap-5(v-else)
+ //.font-outfit {{ pageDataDB.visitation_description }}
+ .flex.row.gap-2
+ .font-outfit.gap-y-5 {{ "400 Freeport Pkwy, Coppell, TX 75019" }}
+ .flex-row.gap-2
+ .flex.flex-col(v-else)
.text-gray-dark.font-poppins.text-2xl.text-left.font-bold(style="line-height: 36px; text-shadow: 3px 3px 4px rgba(0, 0, 0, 0.25);") Visitation
- .flex.gap-5
- .font-outfit {{ "Date: TBD"}}
- .flex.gap-5
- .font-outfit {{ "Location: TBD" }}
- .flex.flex-col.gap-5(v-if="pageDataDB.funeral_date")
- .text-gray-dark.font-poppins.text-2xl.text-left.font-bold(style="line-height: 36px; text-shadow: 3px 3px 4px rgba(0, 0, 0, 0.25);") Funeral
- .flex.gap-5
- .font-outfit {{ "Date:" }}
- .font-outfit {{ dateFormat(pageDataDB.funeral_date, true) }}
- .flex.gap-5
- .font-outfit {{ "Location:" }}
+ .flex.row.gap-2
+ .font-outfit.font-bold {{ "Date:" }}
+ .font-outfit.gap-y-5 {{ "TBD" }}
+ .flex.row.gap-2
+ .font-outfit.font-bold {{ "Time:" }}
+ .font-outfit.gap-y-5 {{ "TBD" }}
+ .flex.row.gap-2
+ .font-outfit.font-bold {{ "Location:" }}
+ .font-outfit.gap-y-5 {{ "TBD" }}
+ .flex.row.gap-2
+ .font-outfit.font-bold {{ "Address:" }}
+ .font-outfit.gap-y-5 {{ "TBD" }}
+ .flex-row.gap-2
+ .flex.flex-col(v-if="pageDataDB.funeral_date")
+ .text-gray-dark.font-poppins.text-2xl.text-left.font-bold(style="line-height: 36px; text-shadow: 3px 3px 4px rgba(0, 0, 0, 0.25);") Funeral Service
+ .flex.row.gap-2
+ .font-outfit.font-bold {{ "Date:" }}
+ .font-outfit.gap-2 {{ longDateFormat(pageDataDB.funeral_date) }}
+ .flex.row.gap-2
+ .font-outfit.font-bold {{ "Time:" }}
+ .font-outfit.gap-y-5 {{ longDateFormat(pageDataDB.funeral_date, true) }}
+ .flex.row.gap-2
+ .font-outfit.font-bold {{ "Location:" }}
.font-outfit.whitespace-normal {{ pageDataDB.funeral_location }}
- .font-outfit {{ pageDataDB.funeral_description }}
- .flex.flex-col.gap-5(v-else)
- .text-gray-dark.font-poppins.text-2xl.text-left.font-bold(style="line-height: 36px; text-shadow: 3px 3px 4px rgba(0, 0, 0, 0.25);") Funeral
- .flex.gap-5
- .font-outfit {{ "Date: TBD" }}
- .flex.gap-5
- .font-outfit {{ "Location: TBD" }}
+ //.font-outfit {{ pageDataDB.funeral_description }}
+ .flex.row.gap-2
+ .font-outfit.font-bold {{ "Address:" }}
+ .font-outfit.gap-y-5 {{ "TBD" }}
+ .flex-row
+ .flex.flex-col(v-else)
+ .text-gray-dark.font-poppins.text-2xl.text-left.font-bold(style="line-height: 36px; text-shadow: 3px 3px 4px rgba(0, 0, 0, 0.25);") Funeral Service
+ .flex.row.gap-2
+ .font-outfit.font-bold {{ "Date:" }}
+ .font-outfit.gap-y-5 {{ "TBD" }}
+ .flex.row.gap-2
+ .font-outfit.font-bold {{ "Time:" }}
+ .font-outfit.gap-y-5 {{ "TBD" }}
+ .flex.row.gap-2
+ .font-outfit.font-bold {{ "Location:" }}
+ .font-outfit.gap-y-5 {{ "TBD" }}
+ .flex.row.gap-2
+ .font-outfit.font-bold {{ "Address:" }}
+ .font-outfit.gap-y-5 {{ "TBD" }}
+ .container.m-4.place-content-center.font-poppins(class="w-5/6 sm:m-auto sm:py-3")
+ .well.well-sm
+ h1.ml-4.pt-9.text-2xl.text-gray-dark(class="sm:text-3xl" style="font-weight: 600; letter-spacing: 0.35px;") Donations
+ // add donation description
+ .col-md-8.ml-4.pt-6.pr-5.flex.items-center.justify-center
+ ActionButton.mx-auto.text-md(name='submit' @click="DisplayDonationPopup=true" class="transition duration-300 bg-orange-999 hover:bg-green-600" ) DONATE NOW
//.container(class="sm:overflow-hidden sm:w-3/4 sm:mt-4 sm:mx-auto sm:place-content-center sm:max-w-xl sm:p-6 sm:rounded-card sm:shadow-card")
.grid(class="sm:grid-cols-2")
- .container.m-4.place-content-center.font-poppins(class="w-5/6 sm:m-auto sm:py-3")
- .well.well-sm
- h1.ml-4.pt-9.text-2xl.text-gray-dark(class="sm:text-3xl" style="font-weight: 600; letter-spacing: 0.35px;") Donations
- .col-md-8.ml-4.pt-6.pr-5.flex.items-center.justify-center
- ActionButton.mx-auto.text-md(name='submit' @click="DisplayDonationPopup=true" class="transition duration-300 bg-orange-999 hover:bg-green-600" ) DONATE NOW
.py-4.grid.gap-1(v-if="comments?.length" style="text-align: center")
.div.py-4.grid(class="w-full" style="grid-template-columns: repeat(3, 1fr);")
.div(v-for="(comment, i) in comments" :key="i" class="comment-box")
diff --git a/prisma/migrations/20240715194925_service_location_names/migration.sql b/prisma/migrations/20240715194925_service_location_names/migration.sql
new file mode 100644
index 00000000..2299856b
--- /dev/null
+++ b/prisma/migrations/20240715194925_service_location_names/migration.sql
@@ -0,0 +1,3 @@
+-- AlterTable
+ALTER TABLE "pages" ADD COLUMN "funeral_address" TEXT NOT NULL DEFAULT '',
+ADD COLUMN "visitation_address" TEXT NOT NULL DEFAULT '';
diff --git a/prisma/migrations/20240715202032_donation_description/migration.sql b/prisma/migrations/20240715202032_donation_description/migration.sql
new file mode 100644
index 00000000..f4371804
--- /dev/null
+++ b/prisma/migrations/20240715202032_donation_description/migration.sql
@@ -0,0 +1,2 @@
+-- AlterTable
+ALTER TABLE "pages" ADD COLUMN "donation_description" TEXT NOT NULL DEFAULT '';
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 0a2eb689..78345676 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -56,12 +56,15 @@ model Page {
day_of_passing DateTime?
visitation_date DateTime?
visitation_location String
+ visitation_address String @default("")
visitation_description String
funeral_date DateTime?
funeral_location String
funeral_description String
+ funeral_address String @default("")
obituary String
donation_goal Int @default(0)
+ donation_description String @default("")
amount_raised Int @default(0)
deadline DateTime?
userCuid String @map("user_cuid")
diff --git a/types.d.ts b/types.d.ts
index 19db4898..7f8419ac 100644
--- a/types.d.ts
+++ b/types.d.ts
@@ -57,13 +57,16 @@ export type Page = {
day_of_passing: Date | string | null,
visitation_date: Date | string | null,
visitation_location: string,
+ visitation_address: string,
visitation_description: string,
funeral_date: Date | string | null,
funeral_description: string,
funeral_location: string,
+ funeral_address: string,
obituary: string,
deadline: Date | string | null,
donation_goal: number | string
+ donation_description: string,
amount_raised: number | string
amount_distributed: number | string
profileImageCuid: string
diff --git a/utils.ts b/utils.ts
index 3b291410..d9439623 100644
--- a/utils.ts
+++ b/utils.ts
@@ -28,4 +28,25 @@ export function donationFormat(amount = 0){
style: "currency",
currency: "usd"
})
-}
\ No newline at end of file
+}
+
+export function longDateFormat(date: string, justTime = false) {
+ if(date === "") {
+ return ""
+ }
+ if(date == null) {
+ return ""
+ }
+
+ const options: Intl.DateTimeFormatOptions = {
+ weekday: 'long',
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric',
+ };
+
+ const dateObj = new Date(date);
+ if(justTime) return dateObj.toLocaleTimeString()
+ return dateObj.toLocaleDateString(undefined, options);
+}
+
From d64673b5cf17bea70c8790ea099272d98cb68c2b Mon Sep 17 00:00:00 2001
From: Isi Emordi
Date: Wed, 17 Jul 2024 14:51:44 -0500
Subject: [PATCH 11/47] created email list page and get authorization code api
---
components/CVHeader.vue | 2 ++
nuxt.config.ts | 2 ++
pages/EmailList.vue | 13 +++++++++++++
server/api/constant_contacts.ts | 15 ++++++++-------
4 files changed, 25 insertions(+), 7 deletions(-)
create mode 100644 pages/EmailList.vue
diff --git a/components/CVHeader.vue b/components/CVHeader.vue
index 601afd80..14751cf9 100644
--- a/components/CVHeader.vue
+++ b/components/CVHeader.vue
@@ -41,6 +41,8 @@ ClientOnly
p.uppercase.white.w-max New page
NavLinkButton( v-if="isAdvocateAdmin" to='/Users' :class="{'!text-black border-green-999 bg-white': route.path.includes('/Users')}")
p.uppercase.white.w-max Users
+ NavLinkButton( v-if="isAdvocateAdmin" to='/EmailList' :class="{'!text-black border-green-999 bg-white': route.path.includes('/EmailList')}")
+ p.uppercase.white.w-max Email List
NavLinkButton( v-if="isAdvocateAdmin" to='/EditUser/0' :class="{'!text-black border-green-999 bg-white': route.path.includes('/EditUser')}")
p.uppercase.white.w-max Invite user
NavLinkButton( v-if="isAdvocateAdmin" to='/EditFamily' :class="{'!text-black border-green-999 bg-white': route.path == '/EditFamily'}")
diff --git a/nuxt.config.ts b/nuxt.config.ts
index b2bf93fd..4f4c97fb 100644
--- a/nuxt.config.ts
+++ b/nuxt.config.ts
@@ -3,6 +3,8 @@ export default defineNuxtConfig({
runtimeConfig: {
AUTH0_CLIENTID: '',
AUTH0_SECRET: '',
+ CONSTANT_CONTACTS_CLIENTID: '',
+ CONSTANT_CONTACTS_SECRET: '',
BASEURL: 'asd',
ISSUER: '',
STRIPE_PUBLIC: '',
diff --git a/pages/EmailList.vue b/pages/EmailList.vue
new file mode 100644
index 00000000..f38c54d8
--- /dev/null
+++ b/pages/EmailList.vue
@@ -0,0 +1,13 @@
+
+
+
+button.type-button.my-4.bg-orange-999.text-white.px-4.py-2.rounded-full.w-32.grow-0(type="button" @click="getAuthRequestUrl") Authorize
+
diff --git a/server/api/constant_contacts.ts b/server/api/constant_contacts.ts
index caa8b17e..81724a28 100644
--- a/server/api/constant_contacts.ts
+++ b/server/api/constant_contacts.ts
@@ -4,12 +4,13 @@ const state = {};
const genState = () => { const s = nanoid(); state[s] = 1; return s}
const runtime = useRuntimeConfig()
-export const authRequestUrl = () => `https://authz.constantcontact.com/oauth2/default/v1/authorize?client_id=${runtime.CONSTANT_CONTACT_CLIENTID}&redirect_uri=${runtime.BASE_URL}&response_type=code&scope=account_read%20account_update%20contact_data%20offline_access&state=${body.state}&nonce=${genState()}`
+const nanoidString = nanoid()
+
+ export default defineEventHandler(async event => {
+ if (event.context.user.user_role === "admin") {
+ const authRequestUrl = () => `https://authz.constantcontact.com/oauth2/default/v1/authorize?client_id=${runtime.CONSTANT_CONTACTS_CLIENTID}&redirect_uri=${encodeURIComponent(runtime.BASEURL)}&response_type=code&scope=account_read%20account_update%20contact_data%20offline_access&state=${nanoidString}`
+
+ await sendRedirect(event, authRequestUrl())
-export const verifyNonce = (nonce: string) => {
- if (state[nonce]) {
- delete state[nonce]
- return true
}
- return false
- }
\ No newline at end of file
+ })
\ No newline at end of file
From 9ce3582568fe05390f415865b50b5ed762dbd269 Mon Sep 17 00:00:00 2001
From: Isi Emordi
Date: Mon, 22 Jul 2024 13:50:47 -0500
Subject: [PATCH 12/47] started writing callback api for constant contacts
---
server/api/cc_callback.ts | 35 +++++++++++++++++++++++++++++++++
server/api/constant_contacts.ts | 3 ++-
2 files changed, 37 insertions(+), 1 deletion(-)
create mode 100644 server/api/cc_callback.ts
diff --git a/server/api/cc_callback.ts b/server/api/cc_callback.ts
new file mode 100644
index 00000000..9581bbe2
--- /dev/null
+++ b/server/api/cc_callback.ts
@@ -0,0 +1,35 @@
+
+const runtime = useRuntimeConfig()
+const getQuery = (event) => {
+ const urlParams = new URLSearchParams(window.location.search);
+ return urlParams
+ }
+
+export default defineEventHandler(async event => {
+ const query = getQuery(event)
+ const authCode = query.code
+
+ if (authCode) {
+ console.log(authCode)
+ } else {
+ console.log('Authorization code not found')
+ }
+
+ const body = new URLSearchParams({
+ code: authCode,
+ redirect_uri: runtime.BASEURL,
+ grant_type: 'authorization_code'
+ })
+
+ const headers = new Headers({
+ "Accept", "application/json"
+ "Content-Type", "application/x-www-form-urlencoded"
+ })
+
+ const response = await fetch(`https://authz.constantcontact.com/oauth2/default/v1/token`, {
+ method: 'POST',
+ body: body.toString(),
+ headers: headers
+ })
+ })
+
\ No newline at end of file
diff --git a/server/api/constant_contacts.ts b/server/api/constant_contacts.ts
index 81724a28..79667907 100644
--- a/server/api/constant_contacts.ts
+++ b/server/api/constant_contacts.ts
@@ -5,10 +5,11 @@ const genState = () => { const s = nanoid(); state[s] = 1; return s}
const runtime = useRuntimeConfig()
const nanoidString = nanoid()
+const redirectURI = `${runtime.BASEURL}/api/cc_callback`
export default defineEventHandler(async event => {
if (event.context.user.user_role === "admin") {
- const authRequestUrl = () => `https://authz.constantcontact.com/oauth2/default/v1/authorize?client_id=${runtime.CONSTANT_CONTACTS_CLIENTID}&redirect_uri=${encodeURIComponent(runtime.BASEURL)}&response_type=code&scope=account_read%20account_update%20contact_data%20offline_access&state=${nanoidString}`
+ const authRequestUrl = () => `https://authz.constantcontact.com/oauth2/default/v1/authorize?client_id=${runtime.CONSTANT_CONTACTS_CLIENTID}&redirect_uri=${encodeURIComponent(redirectURI)}&response_type=code&scope=account_read%20account_update%20contact_data%20offline_access&state=${nanoidString}`
await sendRedirect(event, authRequestUrl())
From 7c84beac3ee2fb0887f16da37ee274ea72adb344 Mon Sep 17 00:00:00 2001
From: Isi Emordi
Date: Mon, 22 Jul 2024 14:59:04 -0500
Subject: [PATCH 13/47] trying to get authorization code from constant contacts
---
server/api/cc_callback.ts | 37 ++++++++++++++-------------------
server/api/constant_contacts.ts | 2 +-
2 files changed, 17 insertions(+), 22 deletions(-)
diff --git a/server/api/cc_callback.ts b/server/api/cc_callback.ts
index 9581bbe2..39ae446d 100644
--- a/server/api/cc_callback.ts
+++ b/server/api/cc_callback.ts
@@ -1,35 +1,30 @@
const runtime = useRuntimeConfig()
-const getQuery = (event) => {
- const urlParams = new URLSearchParams(window.location.search);
- return urlParams
- }
+
+const creds = runtime.CONSTANT_CONTACTS_CLIENTID + ':' + runtime.CONSTANT_CONTACTS_SECRET
+const encodedCreds = btoa(creds)
export default defineEventHandler(async event => {
- const query = getQuery(event)
- const authCode = query.code
-
- if (authCode) {
- console.log(authCode)
+ const { code } = getQuery(event)
+
+ if (code) {
+ console.log(code)
} else {
console.log('Authorization code not found')
}
- const body = new URLSearchParams({
- code: authCode,
- redirect_uri: runtime.BASEURL,
- grant_type: 'authorization_code'
- })
+
- const headers = new Headers({
- "Accept", "application/json"
- "Content-Type", "application/x-www-form-urlencoded"
- })
- const response = await fetch(`https://authz.constantcontact.com/oauth2/default/v1/token`, {
+ const response = await fetch(`https://authz.constantcontact.com/oauth2/default/v1/token?code=${code as string}&redirect_uri=${encodeURIComponent(`${runtime.BASEURL}EmailList`)}&grant_type=authorization_code`, {
method: 'POST',
- body: body.toString(),
- headers: headers
+ headers: {
+ "Accept": "application/json",
+ "Content-Type": "application/x-www-form-urlencoded",
+ "Authorization": `Basic ${encodedCreds}`
+ }
})
+
+ console.log(response)
})
\ No newline at end of file
diff --git a/server/api/constant_contacts.ts b/server/api/constant_contacts.ts
index 79667907..dd542267 100644
--- a/server/api/constant_contacts.ts
+++ b/server/api/constant_contacts.ts
@@ -5,7 +5,7 @@ const genState = () => { const s = nanoid(); state[s] = 1; return s}
const runtime = useRuntimeConfig()
const nanoidString = nanoid()
-const redirectURI = `${runtime.BASEURL}/api/cc_callback`
+const redirectURI = `${runtime.BASEURL}api/cc_callback`
export default defineEventHandler(async event => {
if (event.context.user.user_role === "admin") {
From 0ecfcfe148eba3f1dcc5abf904701ba18eda1f84 Mon Sep 17 00:00:00 2001
From: Alonso Toji
Date: Mon, 22 Jul 2024 15:46:35 -0500
Subject: [PATCH 14/47] finish editing page view and add toggle for headers
---
components/CVHeader.vue | 32 ++++++++--
components/CVReplySystem.vue | 7 ++-
pages/Page/[id].vue | 110 +++++++++++++++--------------------
3 files changed, 76 insertions(+), 73 deletions(-)
diff --git a/components/CVHeader.vue b/components/CVHeader.vue
index f5a93b53..791783f0 100644
--- a/components/CVHeader.vue
+++ b/components/CVHeader.vue
@@ -14,6 +14,7 @@ const pages = ref([])
const searchQuery = ref('');
const route = useRoute()
const isNotSearch = computed(() => route.path !== "/Search/")
+const toggle = ref(true);
console.log(route.path)
const onEnter = async() => {
@@ -28,12 +29,16 @@ const onEnter = async() => {
ClientOnly
.max-w-min.mx-auto.flex.gap-2.mt-7(style="text-align:center")
- div.max-w-min.mx-auto.flex.gap-2(v-if="isLoggedIn")
+ div.max-w-min.mx-auto.flex.gap-2(v-if="isLoggedIn && toggle")
+ NavLinkButton(to='https://carsonsvillage.org' :class="{'!text-black border-green-999 bg-white': route.path == '/'}")
+ p.uppercase.white.w-max 💚
a.items-center.px-2.py-2.text-base.font-medium.rounded-md.text-blue-999.cursor-pointer(
class='hover:text-black bg-white'
href="/api/logout"
)
p.uppercase.white.w-max.font-bold.text-orange-999 LOGOUT
+ NavLinkButton(:class="{'!text-black border-green-999 bg-white': route.path == '/'}" @click="toggle = !toggle")
+ p.uppercase.white.w-max Home
NavLinkButton(:to="`/pageList/${cuid}/?fromUsers=0`" v-if="isAdvocateAdmin" :class="{'!text-black border-green-999 bg-white': route.path.includes('/page') || route.path.includes('/Page')}")
p.uppercase.white.w-max Pages
NavLinkButton(:to="`/pageList/${familyCuid}/?fromUsers=0`" v-if="!isAdvocateAdmin" :class="{'!text-black border-green-999 bg-white': route.path.includes('/pageList')}")
@@ -47,11 +52,29 @@ ClientOnly
NavLinkButton( v-if="isAdvocateAdmin" to='/EditFamily' :class="{'!text-black border-green-999 bg-white': route.path == '/EditFamily'}")
p.uppercase.white.w-max Create Family
NavLinkButton(to="/" :class="{'!text-black border-green-999 bg-white': route.path == '/'}")
- p.uppercase.white.w-max Home
+ p.uppercase.white.w-max Profile
NavLinkButton(v-if="isAdmin" to='/FamilyTransactionList' :class="{'!text-black border-green-999 bg-white': route.path == '/FamilyTransactionList'}")
p.uppercase.white.w-max Donations
NavLinkButton(v-if="isAdvocateAdmin" to='/FamilyReports' :class="{'!text-black border-green-999 bg-white': route.path == '/FamilyReports'}")
p.uppercase.white.w-max Family Reports
+
+ div.max-w-min.mx-auto.flex.gap-2(v-else-if="isLoggedIn && !toggle")
+ a.items-center.px-2.py-2.text-base.font-medium.rounded-md.text-blue-999.cursor-pointer(
+ class='hover:text-black bg-white'
+ href="/api/logout"
+ )
+ p.uppercase.white.w-max.font-bold.text-orange-999 LOGOUT
+ NavLinkButton(:class="{'!text-black border-green-999 bg-white': route.path == '/'}" @click="toggle = !toggle")
+ p.uppercase.white.w-max Dashboard
+ DropdownMenu(:has-submenus="true" :num-submenus="4"
+ :submenus="[{ title: 'Timeline of Important Events', to: 'https://carsonsvillage.org/timeline-of-events/' }, { title: 'Resource Library', to: 'http://carsonsvillage.org/resource-library' }, { title: 'Group Support', to: 'https://carsonsvillage.org/grief-group-support/' }, { title: 'Find Support', to: 'https://carsonsvillage.org/support/' }]" :dropdownMinWidth="200")
+ | RESOURCES
+ NavLinkButton(to='https://carsonsvillage.org/get-involved/' target="_blank")
+ p.uppercase.white.w-max GET INVOLVED
+ DropdownMenu(:has-submenus="true" :num-submenus="6"
+ :submenus="[{ title: 'Our Story', to: 'https://carsonsvillage.org/about-us/our-family/' }, { title: 'Our Testimonies', to: 'https://carsonsvillage.org/our-testimonials/' }, { title: 'In The News', to: 'https://carsonsvillage.org/about-us/in-the-news/' }, { title: 'Newsletter Archive', to: 'https://carsonsvillage.org/about-us/newsletter-archive/' }, { title: 'Our Team >', submenus: [{ title: 'Advocates', to: 'https://carsonsvillage.org/about-us/advocates/' }, { title: 'Clinical Consultants', to: 'https://carsonsvillage.org/about-us/clinical-consultants/' }, { title: 'Support Team', to: 'https://carsonsvillage.org/about-us/meet-our-team/' }]}, { title: 'Board of Directors', to: 'https://carsonsvillage.org/about-us/board-of-directors/' }]" :dropdownMinWidth="150" :nestedDropdownMinWidth="150")
+ | ABOUT US
+
div.max-w-min.mx-auto.flex.gap-2(v-else)
a.items-center.px-2.py-2.text-base.font-medium.rounded-md.text-gray-999.cursor-pointer(
class='hover:!text-black bg-white'
@@ -61,20 +84,17 @@ ClientOnly
NavLinkButton(to='https://carsonsvillage.org/' target="_blank")
p.uppercase.white.w-max HOME
-
DropdownMenu(:has-submenus="true" :num-submenus="4"
:submenus="[{ title: 'Timeline of Important Events', to: 'https://carsonsvillage.org/timeline-of-events/' }, { title: 'Resource Library', to: 'http://carsonsvillage.org/resource-library' }, { title: 'Group Support', to: 'https://carsonsvillage.org/grief-group-support/' }, { title: 'Find Support', to: 'https://carsonsvillage.org/support/' }]" :dropdownMinWidth="200")
| RESOURCES
-
NavLinkButton(to='https://carsonsvillage.org/get-involved/' target="_blank")
p.uppercase.white.w-max GET INVOLVED
-
DropdownMenu(:has-submenus="true" :num-submenus="6"
:submenus="[{ title: 'Our Story', to: 'https://carsonsvillage.org/about-us/our-family/' }, { title: 'Our Testimonies', to: 'https://carsonsvillage.org/our-testimonials/' }, { title: 'In The News', to: 'https://carsonsvillage.org/about-us/in-the-news/' }, { title: 'Newsletter Archive', to: 'https://carsonsvillage.org/about-us/newsletter-archive/' }, { title: 'Our Team >', submenus: [{ title: 'Advocates', to: 'https://carsonsvillage.org/about-us/advocates/' }, { title: 'Clinical Consultants', to: 'https://carsonsvillage.org/about-us/clinical-consultants/' }, { title: 'Support Team', to: 'https://carsonsvillage.org/about-us/meet-our-team/' }]}, { title: 'Board of Directors', to: 'https://carsonsvillage.org/about-us/board-of-directors/' }]" :dropdownMinWidth="150" :nestedDropdownMinWidth="150")
| ABOUT US
//&& isLoggedIn")
- .flex.w-max(v-if="isNotSearch")
+ .flex.w-max.px-2(v-if="isNotSearch")
input(class="border border-gray-300 py-2 px-4 rounded-lg focus:outline-none focus:border-black-500"
type="search" placeholder=" " v-model="searchQuery" v-on:keyup.enter="onEnter" style="height: 40px")
NuxtLink.inline(:to="`/Search/?search=${searchQuery}&isPageList=0`")
diff --git a/components/CVReplySystem.vue b/components/CVReplySystem.vue
index f7a1b5c3..c4b50bbc 100644
--- a/components/CVReplySystem.vue
+++ b/components/CVReplySystem.vue
@@ -55,9 +55,10 @@ const submitComment = async () => {
.comment-system.flex.flex-col.items-center(class="sm:mx-4 sm:w-full sm:py-2")
h2.text-center.mt-4.mb-6.font-bold Leave a Message
- CVTextArea(name='reply' v-model="replyData.reply" placeholder='Replies' class="font-normal h-40 w-full")
- .field-row.flex.mt-4.w-full
- CVInput(name='name' v-model="replyData.name" placeholder='Name' class="font-normal w-full")
+ .w-full.flex.justify-center
+ CVTextArea(name='reply' v-model="replyData.reply" placeholder='Replies' class="font-normal h-40 w-2/3")
+ .field-row.flex.justify-center.mt-4.w-full
+ CVInput(name='name' v-model="replyData.name" placeholder='Name' class="font-normal w-2/3" )
.col-md-8.ml-4.pt-6.pr-5.flex.items-center.justify-center.mt-6
ActionButton.mx-auto.text-md(@click="submitComment" class="transition duration-300 bg-orange-999 hover:bg-green-600") Submit
.div(v-if="successMessage" class="mt-4 text-green-500") {{ successMessage }}
diff --git a/pages/Page/[id].vue b/pages/Page/[id].vue
index 473285c0..109d40fd 100644
--- a/pages/Page/[id].vue
+++ b/pages/Page/[id].vue
@@ -220,31 +220,31 @@ setImageAutoSlide()
// donation popup
-.div.flex(@click.self="DisplayDonationPopup=false" v-if="DisplayDonationPopup" style="z-index: 10; align-items: center; justify-content: center; position: fixed; top: 0; bottom: 0; left: 0; right: 0; background: rgba(0, 0, 0, 0.7);")
- .mx-auto.flex.p-2(style="width:50%; background-color:#fff;")
- DonationEntryPopup(@Exit="exitPopup" :amount_raised="pageDataDB.amount_raised" :donation_goal="pageDataDB.donation_goal" :donation_goal_provided="donation_goal_provided" :donated_percentage="donated_percentage" :isActive="isActive" :donationData="donationData" :pageCuid="pageCuid" :familyCuid="familyCuid")
-
-// the header overlay with image and name
-
+.flex.flex-col
+ .div.flex(@click.self="DisplayDonationPopup=false" v-if="DisplayDonationPopup" style="z-index: 10; align-items: center; justify-content: center; position: fixed; top: 0; bottom: 0; left: 0; right: 0; background: rgba(0, 0, 0, 0.7);")
+ .mx-auto.flex.p-2(style="width:50%; background-color:#fff;")
+ DonationEntryPopup(@Exit="exitPopup" :amount_raised="pageDataDB.amount_raised" :donation_goal="pageDataDB.donation_goal" :donation_goal_provided="donation_goal_provided" :donated_percentage="donated_percentage" :isActive="isActive" :donationData="donationData" :pageCuid="pageCuid" :familyCuid="familyCuid")
.flex.gap-2.justify-center.cols-2.pl-6.pr-6
a.mr-2.mt-1.p-2.px-9.pt-3.pb-3.bg-orange-999(class="transition duration-300 bg-orange-999 hover:bg-green-600" style="border-radius: 100px; height: 50px; color: white; font-weight: 700;") Archive
.mt-2.min-h-24.text-white.uppercase.w-full(style="background-image: url('https://carsonsvillage.org/wp-content/uploads/2018/11/iStock-862083112-BW.jpg');")
.h-full.py-8.self-center.w-full.text-center.flex.flex-col(style="background-color: rgba(50, 119, 136, .8)")
- p.my-auto.font-bold.text-4xl {{ pageDataDB.page_first_name + " " + pageDataDB.page_last_name }}
+ p.my-auto.font-bold.text-5xl {{ pageDataDB.page_first_name + " " + pageDataDB.page_last_name }}
-.flex.flex-col.gap-5.px-4.mx-auto.mt-8(class="w-3/4 sm:px-16")
- img.mx-auto(v-if="profileImage?.url" class="w-[122px] h-[122px] rounded-[8px]" :src="`${profileImage?.url}`")
- .text-gray-dark.mx-auto.w-max.font-poppins.text-md {{ dateFormat(pageDataDB.day_of_birth, true) + ((pageDataDB.day_of_birth || pageDataDB.day_of_passing) ? ' - ' : '') + dateFormat(pageDataDB.day_of_passing, true) }}
- .flex.flex-col-reverse.gap-5(class="sm:grid sm:grid-cols-2")
+.grid.grid-cols-1(class="sm:grid-cols-2").justify-center.px-2
+ .col-span-2
+ .flex.flex-col.gap-5.px-4.mx-auto.mt-8(class="w-3/4 sm:px-16")
+ img.mx-auto(v-if="profileImage?.url" class="w-[122px] h-[122px] rounded-[8px]" :src="`${profileImage?.url}`")
+ .text-gray-dark.mx-auto.w-max.font-poppins.text-md {{ dateFormat(pageDataDB.day_of_birth, true) + ((pageDataDB.day_of_birth || pageDataDB.day_of_passing) ? ' - ' : '') + dateFormat(pageDataDB.day_of_passing, true) }}
+ .col-span-1.justify-self-end.pr-5.pt-5
.relative.w-96.p-1(v-if="imageData.length != 0" )
button.absolute.left-4.top-64.bg-black.text-white(@click="prevImage" style="opacity:0.7; --tw-text-opacity: 1; width: 46px; height: 46px; border-radius:50%; align-items: center; justify-content: center; line-height: 2; text-align: center;color: white;") <
button.absolute.right-8.top-64.bg-black.text-white(@click="nextImage" style="opacity:0.7; --tw-text-opacity: 1; width: 46px; height: 46px; border-radius:50%; align-items: center; justify-content: center; line-height: 2; text-align: center;color: white;") >
img.w-96(style="z-index: -1; object-fit:cover;" :src="imageData[currentImage].url")
- // services list
- .py-4.flex.flex-col(class="lg:grid lg:grid-rows-6")
- .flex.flex-col(v-if="pageDataDB.visitation_date")
- .text-gray-dark.font-poppins.text-2xl.text-left.font-bold(style="line-height: 36px; text-shadow: 3px 3px 4px rgba(0, 0, 0, 0.25);") Visitation
- .flex.row.gap-2
+ // services list
+ .col-span-1(class="sm:grid-cols-2").pt-5.pl-5.pr-15
+ .flex.flex-col(v-if="pageDataDB.visitation_date").pb-5.pr-15
+ .text-gray-dark.font-poppins.text-3xl.text-left.font-bold(style="line-height: 10px; justify-content: start; text-shadow: 3px 3px 4px rgba(0, 0, 0, 0.25);") Visitation
+ .flex.row.gap-2.pt-2
.font-outfit.flex-col.font-bold {{ "Date:" }}
.font-outfit {{ longDateFormat(pageDataDB.visitation_date) }}
.flex.row.gap-2
@@ -255,25 +255,17 @@ setImageAutoSlide()
.font-outfit.whitespace-normal {{ pageDataDB.visitation_location ? pageDataDB.visitation_location : "TBD" }}
//.font-outfit {{ pageDataDB.visitation_description }}
.flex.row.gap-2
- .font-outfit.gap-y-5 {{ "400 Freeport Pkwy, Coppell, TX 75019" }}
- .flex-row.gap-2
- .flex.flex-col(v-else)
- .text-gray-dark.font-poppins.text-2xl.text-left.font-bold(style="line-height: 36px; text-shadow: 3px 3px 4px rgba(0, 0, 0, 0.25);") Visitation
- .flex.row.gap-2
- .font-outfit.font-bold {{ "Date:" }}
- .font-outfit.gap-y-5 {{ "TBD" }}
- .flex.row.gap-2
- .font-outfit.font-bold {{ "Time:" }}
- .font-outfit.gap-y-5 {{ "TBD" }}
- .flex.row.gap-2
- .font-outfit.font-bold {{ "Location:" }}
- .font-outfit.gap-y-5 {{ "TBD" }}
+ .font-outfit.gap-y-5 {{ pageDataDB.visitation_address }}
+ .flex.row.gap-2.pr-10
+ .font-outfit.font-bold {{ "Description:" }}
+ .font-outfit.whitespace-normal {{ pageDataDB.visitation_description }}
+
+ .flex.flex-col(v-else).pb-5.pr-15
+ .text-gray-dark.font-poppins.text-3xl.text-left.font-bold(style="line-height: 36px; text-shadow: 3px 3px 4px rgba(0, 0, 0, 0.25);") Visitation
.flex.row.gap-2
- .font-outfit.font-bold {{ "Address:" }}
- .font-outfit.gap-y-5 {{ "TBD" }}
- .flex-row.gap-2
- .flex.flex-col(v-if="pageDataDB.funeral_date")
- .text-gray-dark.font-poppins.text-2xl.text-left.font-bold(style="line-height: 36px; text-shadow: 3px 3px 4px rgba(0, 0, 0, 0.25);") Funeral Service
+ .font-outfit {{ "There is no visitation information available at this time." }}
+ .flex.flex-col(v-if="pageDataDB.funeral_date").pb-5.pr-15
+ .text-gray-dark.font-poppins.text-3xl.text-left.font-bold(style="line-height: 36px; text-shadow: 3px 3px 4px rgba(0, 0, 0, 0.25);") Funeral Service
.flex.row.gap-2
.font-outfit.font-bold {{ "Date:" }}
.font-outfit.gap-2 {{ longDateFormat(pageDataDB.funeral_date) }}
@@ -283,33 +275,27 @@ setImageAutoSlide()
.flex.row.gap-2
.font-outfit.font-bold {{ "Location:" }}
.font-outfit.whitespace-normal {{ pageDataDB.funeral_location }}
- //.font-outfit {{ pageDataDB.funeral_description }}
.flex.row.gap-2
- .font-outfit.font-bold {{ "Address:" }}
- .font-outfit.gap-y-5 {{ "TBD" }}
- .flex-row
- .flex.flex-col(v-else)
- .text-gray-dark.font-poppins.text-2xl.text-left.font-bold(style="line-height: 36px; text-shadow: 3px 3px 4px rgba(0, 0, 0, 0.25);") Funeral Service
- .flex.row.gap-2
- .font-outfit.font-bold {{ "Date:" }}
- .font-outfit.gap-y-5 {{ "TBD" }}
- .flex.row.gap-2
- .font-outfit.font-bold {{ "Time:" }}
- .font-outfit.gap-y-5 {{ "TBD" }}
- .flex.row.gap-2
- .font-outfit.font-bold {{ "Location:" }}
- .font-outfit.gap-y-5 {{ "TBD" }}
+ .font-outfit.whitespace-normal {{ pageDataDB.funeral_address }}
+ .flex.row.gap-2.pr-10
+ .font-outfit.font-bold {{ "Description:" }}
+ .font-outfit {{ pageDataDB.funeral_description }}
+
+ .flex.flex-col(v-else).pb-5
+ .text-gray-dark.font-poppins.text-3xl.text-left.font-bold(style="line-height: 36px; text-shadow: 3px 3px 4px rgba(0, 0, 0, 0.25);") Funeral Service
.flex.row.gap-2
- .font-outfit.font-bold {{ "Address:" }}
- .font-outfit.gap-y-5 {{ "TBD" }}
- .container.m-4.place-content-center.font-poppins(class="w-5/6 sm:m-auto sm:py-3")
- .well.well-sm
- h1.ml-4.pt-9.text-2xl.text-gray-dark(class="sm:text-3xl" style="font-weight: 600; letter-spacing: 0.35px;") Donations
- // add donation description
- .col-md-8.ml-4.pt-6.pr-5.flex.items-center.justify-center
+ .font-outfit.gap-y-5 {{ "There is no funeral information avilable at this time" }}
+ .flex.flex-col.pb-5
+ .text-gray-dark.font-poppins.text-3xl.text-left.font-bold(style="line-height: 36px; text-shadow: 3px 3px 4px rgba(0, 0, 0, 0.25);") Donations
+ .text-gray-dark.font-poppins.text-1xl.text-left.font-bold(style="line-height: 36px; text-shadow: 3px 3px 4px rgba(0, 0, 0, 0.25);")
+ .col-md-1.ml-1.pt-9.pr-5.flex.items-center.justify-center
ActionButton.mx-auto.text-md(name='submit' @click="DisplayDonationPopup=true" class="transition duration-300 bg-orange-999 hover:bg-green-600" ) DONATE NOW
+ .col-span-2
+ .col-md-8.mx-9(class="sm:col-span-1 sm:mr-11")
+ .div.px-10.py-4(style="color: #6E6E6E; font-weight: 500; font-size: 18px; line-height: 28px; letter-spacing: -0.078px; word-break: break-word;" id="obituary") {{ pageDataDB.obituary }}
+
//.container(class="sm:overflow-hidden sm:w-3/4 sm:mt-4 sm:mx-auto sm:place-content-center sm:max-w-xl sm:p-6 sm:rounded-card sm:shadow-card")
-.grid(class="sm:grid-cols-2")
+.grid.grid-cols-1(class="sm:grid-cols-1" )
.py-4.grid.gap-1(v-if="comments?.length" style="text-align: center")
.div.py-4.grid(class="w-full" style="grid-template-columns: repeat(3, 1fr);")
.div(v-for="(comment, i) in comments" :key="i" class="comment-box")
@@ -318,10 +304,10 @@ setImageAutoSlide()
p.comment-body(style="font-size: 0.75rem; width: fit-content; color: #666;") {{ comment.comments }}
.div.comment-donation-amount(style="font-size: 0.75rem; color: #666;") Amount Donated: {{ donationFormat(comment.amount) }}
CVReplySystem(:pageCuid="id" :familyCuid="familyCuid" :replies="replies" @displayReply="displayReply")
- .py-4.grid.flex-box.flex-row.item-centered.gap-1(v-if="replies?.length" style="line-height: 0px;text-align: center")
+ .py-4.grid.flex-box.flex-row.gap-1(v-if="replies?.length" style="line-height: 0px;text-align: center")
div(class="flex")
- .div(v-for="(reply,i) in replies" :key="i" class="reply-box")
- .reply-box(v-if="reply.reply.length > 0" style="padding: 1rem; text-align: left; border-bottom: 1px solid black")
+ .div(v-for="(reply,i) in replies" :key="i" class="reply-box" )
+ .px-10.reply-box(v-if="reply.reply.length > 0" style="padding: 1rem; text-align: left; border-bottom: 1px solid black")
.reply-header(style="font-size: 1rem; font-weight: bold; margin-bottom: 2.5rem; margin-left: 1rem") {{reply.name}}
.reply-body(style="font-size: 1rem; color: #666; margin-bottom: 2.5rem;") {{reply.reply}}
div.flex(style="color:gray; font-weight: 700; justify-content:center; align-items: center; height: 100px;")
@@ -335,8 +321,4 @@ div.flex(style="color:gray; font-weight: 700; justify-content:center; align-item
.col
button(@click="shareMail")
img(src="/mail_fa.png" style="width:50px; height:29px;")
- .col
- p {{ "" }}
- .col-md-8.mx-9(class="sm:col-span-1 sm:mr-11")
- .div.px-8.py-4(style="color: #6E6E6E; font-weight: 500; font-size: 18px; line-height: 28px; letter-spacing: -0.078px; word-break: break-word;" id="obituary") {{ pageDataDB.obituary }}
From 4d3857da3d9d36cffc242cfa0799376dfb9758f0 Mon Sep 17 00:00:00 2001
From: Isi Emordi
Date: Wed, 24 Jul 2024 13:10:19 -0500
Subject: [PATCH 15/47] added the auth token to the database
---
.../migration.sql | 8 ++++++++
prisma/schema.prisma | 6 ++++++
2 files changed, 14 insertions(+)
create mode 100644 prisma/migrations/20240722200429_constant_contacts_access_token/migration.sql
diff --git a/prisma/migrations/20240722200429_constant_contacts_access_token/migration.sql b/prisma/migrations/20240722200429_constant_contacts_access_token/migration.sql
new file mode 100644
index 00000000..f2781052
--- /dev/null
+++ b/prisma/migrations/20240722200429_constant_contacts_access_token/migration.sql
@@ -0,0 +1,8 @@
+-- CreateTable
+CREATE TABLE "CC_Token" (
+ "cuid" TEXT NOT NULL,
+ "token" TEXT NOT NULL,
+ "date" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+
+ CONSTRAINT "CC_Token_pkey" PRIMARY KEY ("cuid")
+);
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 0a2eb689..bfe519bf 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -104,6 +104,12 @@ model PageDonation {
@@map("page_donations")
}
+model CC_Token {
+ cuid String @id @default(cuid())
+ token String
+ date DateTime @default(now())
+}
+
model Reply {
cuid String @id @default(cuid())
pageCuid String @map("page_cuid")
From d19ad3ded431419c409f6c694f53627885f3d262 Mon Sep 17 00:00:00 2001
From: Isi Emordi
Date: Wed, 24 Jul 2024 13:43:24 -0500
Subject: [PATCH 16/47] fixed database and callback api
---
prisma/schema.prisma | 1 +
server/api/cc_callback.ts | 11 ++++++-----
2 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index bfe519bf..0ca16076 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -107,6 +107,7 @@ model PageDonation {
model CC_Token {
cuid String @id @default(cuid())
token String
+ refresh_token String
date DateTime @default(now())
}
diff --git a/server/api/cc_callback.ts b/server/api/cc_callback.ts
index 39ae446d..f0973ddf 100644
--- a/server/api/cc_callback.ts
+++ b/server/api/cc_callback.ts
@@ -13,10 +13,7 @@ export default defineEventHandler(async event => {
console.log('Authorization code not found')
}
-
-
-
- const response = await fetch(`https://authz.constantcontact.com/oauth2/default/v1/token?code=${code as string}&redirect_uri=${encodeURIComponent(`${runtime.BASEURL}EmailList`)}&grant_type=authorization_code`, {
+ const response = await fetch(`https://authz.constantcontact.com/oauth2/default/v1/token?code=${code as string}&redirect_uri=${encodeURIComponent(`${runtime.BASEURL}api/cc_callback`)}&grant_type=authorization_code`, {
method: 'POST',
headers: {
"Accept": "application/json",
@@ -25,6 +22,10 @@ export default defineEventHandler(async event => {
}
})
- console.log(response)
+ const accessToken = (await response.json()).access_token
+
+
+
+ console.log(accessToken)
})
\ No newline at end of file
From d5fb857b38e1a42e4c1f7d9ee7719a24d4bb6031 Mon Sep 17 00:00:00 2001
From: Isi Emordi
Date: Wed, 24 Jul 2024 14:30:09 -0500
Subject: [PATCH 17/47] completed oauth2 flow of constant contacts
---
pages/EmailList.vue | 6 ++-
.../migration.sql | 8 ++++
.../migration.sql | 2 +
prisma/schema.prisma | 2 +-
server/api/cc_callback.ts | 23 ++++++++--
server/api/refresh_token.ts | 45 +++++++++++++++++++
6 files changed, 80 insertions(+), 6 deletions(-)
create mode 100644 prisma/migrations/20240724184351_add_refresh_token_to_cc_token/migration.sql
create mode 100644 prisma/migrations/20240724185315_default_token_id/migration.sql
create mode 100644 server/api/refresh_token.ts
diff --git a/pages/EmailList.vue b/pages/EmailList.vue
index f38c54d8..f9a2f628 100644
--- a/pages/EmailList.vue
+++ b/pages/EmailList.vue
@@ -1,13 +1,17 @@
button.type-button.my-4.bg-orange-999.text-white.px-4.py-2.rounded-full.w-32.grow-0(type="button" @click="getAuthRequestUrl") Authorize
+button.type-button.my-4.bg-orange-999.text-white.px-4.py-2.rounded-full.w-32.grow-0(type="button" @click="refreshToken") Refresh
diff --git a/prisma/migrations/20240724184351_add_refresh_token_to_cc_token/migration.sql b/prisma/migrations/20240724184351_add_refresh_token_to_cc_token/migration.sql
new file mode 100644
index 00000000..90d7a747
--- /dev/null
+++ b/prisma/migrations/20240724184351_add_refresh_token_to_cc_token/migration.sql
@@ -0,0 +1,8 @@
+/*
+ Warnings:
+
+ - Added the required column `refresh_token` to the `CC_Token` table without a default value. This is not possible if the table is not empty.
+
+*/
+-- AlterTable
+ALTER TABLE "CC_Token" ADD COLUMN "refresh_token" TEXT NOT NULL;
diff --git a/prisma/migrations/20240724185315_default_token_id/migration.sql b/prisma/migrations/20240724185315_default_token_id/migration.sql
new file mode 100644
index 00000000..8c989bf1
--- /dev/null
+++ b/prisma/migrations/20240724185315_default_token_id/migration.sql
@@ -0,0 +1,2 @@
+-- AlterTable
+ALTER TABLE "CC_Token" ALTER COLUMN "cuid" SET DEFAULT '0';
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 0ca16076..4f95f2fc 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -105,7 +105,7 @@ model PageDonation {
}
model CC_Token {
- cuid String @id @default(cuid())
+ cuid String @id @default("0")
token String
refresh_token String
date DateTime @default(now())
diff --git a/server/api/cc_callback.ts b/server/api/cc_callback.ts
index f0973ddf..bcb95b54 100644
--- a/server/api/cc_callback.ts
+++ b/server/api/cc_callback.ts
@@ -1,6 +1,5 @@
const runtime = useRuntimeConfig()
-
const creds = runtime.CONSTANT_CONTACTS_CLIENTID + ':' + runtime.CONSTANT_CONTACTS_SECRET
const encodedCreds = btoa(creds)
@@ -22,10 +21,26 @@ export default defineEventHandler(async event => {
}
})
- const accessToken = (await response.json()).access_token
+ const respBody = await response.json()
-
+ const addToken = await event.context.client.CC_Token.upsert({
+ where: {
+ cuid: "0"
+ },
+ update: {
+ token: respBody.access_token,
+ refresh_token: respBody.refresh_token,
+ date: new Date()
+
+ },
+ create: {
+ token: respBody.access_token,
+ refresh_token: respBody.refresh_token,
+ date: new Date()
+
+ }
+ })
- console.log(accessToken)
+ await sendRedirect(event, `/EmailList?success=1`)
})
\ No newline at end of file
diff --git a/server/api/refresh_token.ts b/server/api/refresh_token.ts
new file mode 100644
index 00000000..14023184
--- /dev/null
+++ b/server/api/refresh_token.ts
@@ -0,0 +1,45 @@
+
+
+const runtime = useRuntimeConfig()
+const creds = runtime.CONSTANT_CONTACTS_CLIENTID + ':' + runtime.CONSTANT_CONTACTS_SECRET
+const encodedCreds = btoa(creds)
+export default defineEventHandler(async event => {
+
+ const refreshToken = await event.context.client.CC_Token.findFirst({
+ where: {
+ cuid: "0"
+ }
+ })
+
+ const response = await fetch(`https://authz.constantcontact.com/oauth2/default/v1/token?refresh_token=${refreshToken.refresh_token as string}&grant_type=refresh_token`, {
+ method: 'POST',
+ headers: {
+ "Accept": "application/json",
+ "Content-Type": "application/x-www-form-urlencoded",
+ "Authorization": `Basic ${encodedCreds}`
+ }
+ })
+
+ const respBody = await response.json()
+
+ const addToken = await event.context.client.CC_Token.upsert({
+ where: {
+ cuid: "0"
+ },
+ update: {
+ token: respBody.access_token,
+ refresh_token: respBody.refresh_token,
+ date: new Date()
+
+ },
+ create: {
+ token: respBody.access_token,
+ refresh_token: respBody.refresh_token,
+ date: new Date()
+
+ }
+ })
+
+ await sendRedirect(event, `/EmailList?success=1`)
+ })
+
\ No newline at end of file
From de9d757a67a806f2a78a1b05c42a140ed2aa38cd Mon Sep 17 00:00:00 2001
From: Alonso Toji
Date: Wed, 24 Jul 2024 15:55:23 -0500
Subject: [PATCH 18/47] add CV logo to navbar and finalize edits on the navbar
---
.DS_Store | Bin 0 -> 6148 bytes
components/CVHeader.vue | 39 ++++---
components/DropdownMenu.vue | 193 +++++++++++++++--------------------
components/NavLinkButton.vue | 6 +-
package-lock.json | 24 +++--
package.json | 4 +-
public/CVLogo.png | Bin 0 -> 21392 bytes
public/greenHeart.png | Bin 0 -> 2598 bytes
tailwind.config.js | 8 +-
9 files changed, 131 insertions(+), 143 deletions(-)
create mode 100644 .DS_Store
create mode 100644 public/CVLogo.png
create mode 100644 public/greenHeart.png
diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..0c444cb4ed0fde751068c5916b2c11223959e8f2
GIT binary patch
literal 6148
zcmeHK%}T>S5Z-NTn^J@v6nYGJEm+%96fYsx7cim+m718M!I&*gVh*K{v%Zi|;`2DO
zyEy~{-bCyS?0&QJvzz%K`@$-O61kgtX_h$N>IO%bLPUP4
zd;V3p$mizXnTS&_j2A1J5C#hfxxEhKK;(|d;vkW^u5uV%qdPbI>-A_l99ZMY@n&GH
zPe!0m#?#HFYwRB!o?gtKqnB8`DIz&=E@f9^3Gbj(YI=27aU|kLu$MVyEFm#K3=jjv
z!1ghq&jO>jeWg>y!~iky0|U4}SkMq{jfFzFbwG#LXY{uaQ9#GH1fr$U)>tS64+vMO
zfGU;iCk9vPV7GLft+7z3(ixX4!#sNB^6|pu>R`8IIODcL>WKkjpvpi~4NW}%&*7J;
zedJeDXhaMU1OJQx-kP~H2a3{X>$mdothJ!sLqoy10u>O@E0+Km;6BoqPUW{yhd5hf
Wp%7<5yG#e9i-07AI%41#82AENzDyGU
literal 0
HcmV?d00001
diff --git a/components/CVHeader.vue b/components/CVHeader.vue
index 791783f0..c87e7fd7 100644
--- a/components/CVHeader.vue
+++ b/components/CVHeader.vue
@@ -23,22 +23,26 @@ const onEnter = async() => {
}
-
ClientOnly
- .max-w-min.mx-auto.flex.gap-2.mt-7(style="text-align:center")
+ .max-w-min.mx-auto.pt-3.flex.gap-2.mt-7(style="text-align:center")
div.max-w-min.mx-auto.flex.gap-2(v-if="isLoggedIn && toggle")
- NavLinkButton(to='https://carsonsvillage.org' :class="{'!text-black border-green-999 bg-white': route.path == '/'}")
- p.uppercase.white.w-max 💚
- a.items-center.px-2.py-2.text-base.font-medium.rounded-md.text-blue-999.cursor-pointer(
+ a.w-20.items-center.px-2.py-2.text-sm.font-medium.rounded-md.text-blue-999.cursor-pointer(
+ class='hover:text-black bg-white'
+ target="blank"
+ href="https://carsonsvillage.org"
+ )
+ img.w-20.h-14(src="/CVLogo.png")
+
+ a.items-center.pt-5.px-2.py-2.text-sm.font-medium.rounded-md.text-blue-999.cursor-pointer(
class='hover:text-black bg-white'
href="/api/logout"
)
p.uppercase.white.w-max.font-bold.text-orange-999 LOGOUT
NavLinkButton(:class="{'!text-black border-green-999 bg-white': route.path == '/'}" @click="toggle = !toggle")
- p.uppercase.white.w-max Home
+ p.uppercase.white.w-max.text-blue-999 Main Menu
NavLinkButton(:to="`/pageList/${cuid}/?fromUsers=0`" v-if="isAdvocateAdmin" :class="{'!text-black border-green-999 bg-white': route.path.includes('/page') || route.path.includes('/Page')}")
p.uppercase.white.w-max Pages
NavLinkButton(:to="`/pageList/${familyCuid}/?fromUsers=0`" v-if="!isAdvocateAdmin" :class="{'!text-black border-green-999 bg-white': route.path.includes('/pageList')}")
@@ -59,24 +63,31 @@ ClientOnly
p.uppercase.white.w-max Family Reports
div.max-w-min.mx-auto.flex.gap-2(v-else-if="isLoggedIn && !toggle")
- a.items-center.px-2.py-2.text-base.font-medium.rounded-md.text-blue-999.cursor-pointer(
+ a.w-20.items-center.px-2.py-2.text-sm.font-medium.rounded-md.text-blue-999.cursor-pointer(
+ class='hover:text-black bg-white'
+ target="blank"
+ href="https://carsonsvillage.org"
+ )
+ img.w-20.h-14(src="/CVLogo.png")
+
+ a.items-center.pt-5.px-2.py-2.text-sm.font-medium.rounded-md.text-blue-999.cursor-pointer(
class='hover:text-black bg-white'
href="/api/logout"
)
p.uppercase.white.w-max.font-bold.text-orange-999 LOGOUT
NavLinkButton(:class="{'!text-black border-green-999 bg-white': route.path == '/'}" @click="toggle = !toggle")
- p.uppercase.white.w-max Dashboard
+ p.uppercase.white.w-max.text-blue-999 Dashboard
DropdownMenu(:has-submenus="true" :num-submenus="4"
:submenus="[{ title: 'Timeline of Important Events', to: 'https://carsonsvillage.org/timeline-of-events/' }, { title: 'Resource Library', to: 'http://carsonsvillage.org/resource-library' }, { title: 'Group Support', to: 'https://carsonsvillage.org/grief-group-support/' }, { title: 'Find Support', to: 'https://carsonsvillage.org/support/' }]" :dropdownMinWidth="200")
| RESOURCES
NavLinkButton(to='https://carsonsvillage.org/get-involved/' target="_blank")
- p.uppercase.white.w-max GET INVOLVED
+ p.uppercase.white.w-max.text-black-999 GET INVOLVED
DropdownMenu(:has-submenus="true" :num-submenus="6"
:submenus="[{ title: 'Our Story', to: 'https://carsonsvillage.org/about-us/our-family/' }, { title: 'Our Testimonies', to: 'https://carsonsvillage.org/our-testimonials/' }, { title: 'In The News', to: 'https://carsonsvillage.org/about-us/in-the-news/' }, { title: 'Newsletter Archive', to: 'https://carsonsvillage.org/about-us/newsletter-archive/' }, { title: 'Our Team >', submenus: [{ title: 'Advocates', to: 'https://carsonsvillage.org/about-us/advocates/' }, { title: 'Clinical Consultants', to: 'https://carsonsvillage.org/about-us/clinical-consultants/' }, { title: 'Support Team', to: 'https://carsonsvillage.org/about-us/meet-our-team/' }]}, { title: 'Board of Directors', to: 'https://carsonsvillage.org/about-us/board-of-directors/' }]" :dropdownMinWidth="150" :nestedDropdownMinWidth="150")
| ABOUT US
- div.max-w-min.mx-auto.flex.gap-2(v-else)
- a.items-center.px-2.py-2.text-base.font-medium.rounded-md.text-gray-999.cursor-pointer(
+ div.max-w-min.mx-auto.flex.gap-2.pt-3(v-else)
+ a.items-center.px-2.py-2.text-sm.font-medium.rounded-md.text-gray-999.cursor-pointer(
class='hover:!text-black bg-white'
href="/api/login"
)
@@ -85,16 +96,16 @@ ClientOnly
NavLinkButton(to='https://carsonsvillage.org/' target="_blank")
p.uppercase.white.w-max HOME
DropdownMenu(:has-submenus="true" :num-submenus="4"
- :submenus="[{ title: 'Timeline of Important Events', to: 'https://carsonsvillage.org/timeline-of-events/' }, { title: 'Resource Library', to: 'http://carsonsvillage.org/resource-library' }, { title: 'Group Support', to: 'https://carsonsvillage.org/grief-group-support/' }, { title: 'Find Support', to: 'https://carsonsvillage.org/support/' }]" :dropdownMinWidth="200")
+ :submenus="[{ title: 'Timeline of Important Events', to: 'https://carsonsvillage.org/timeline-of-events/' }, { title: 'Resource Library', to: 'http://carsonsvillage.org/resource-library' }, { title: 'Group Support', to: 'https://carsonsvillage.org/grief-group-support/' }, { title: 'Find Support', to: 'https://carsonsvillage.org/support/' }]" :dropdownMinWidth="225")
| RESOURCES
NavLinkButton(to='https://carsonsvillage.org/get-involved/' target="_blank")
p.uppercase.white.w-max GET INVOLVED
DropdownMenu(:has-submenus="true" :num-submenus="6"
- :submenus="[{ title: 'Our Story', to: 'https://carsonsvillage.org/about-us/our-family/' }, { title: 'Our Testimonies', to: 'https://carsonsvillage.org/our-testimonials/' }, { title: 'In The News', to: 'https://carsonsvillage.org/about-us/in-the-news/' }, { title: 'Newsletter Archive', to: 'https://carsonsvillage.org/about-us/newsletter-archive/' }, { title: 'Our Team >', submenus: [{ title: 'Advocates', to: 'https://carsonsvillage.org/about-us/advocates/' }, { title: 'Clinical Consultants', to: 'https://carsonsvillage.org/about-us/clinical-consultants/' }, { title: 'Support Team', to: 'https://carsonsvillage.org/about-us/meet-our-team/' }]}, { title: 'Board of Directors', to: 'https://carsonsvillage.org/about-us/board-of-directors/' }]" :dropdownMinWidth="150" :nestedDropdownMinWidth="150")
+ :submenus="[{ title: 'Our Story', to: 'https://carsonsvillage.org/about-us/our-family/' }, { title: 'Our Testimonies', to: 'https://carsonsvillage.org/our-testimonials/' }, { title: 'In The News', to: 'https://carsonsvillage.org/about-us/in-the-news/' }, { title: 'Newsletter Archive', to: 'https://carsonsvillage.org/about-us/newsletter-archive/' }, { title: 'Our Team >', submenus: [{ title: 'Advocates', to: 'https://carsonsvillage.org/about-us/advocates/' }, { title: 'Clinical Consultants', to: 'https://carsonsvillage.org/about-us/clinical-consultants/' }, { title: 'Support Team', to: 'https://carsonsvillage.org/about-us/meet-our-team/' }]}, { title: 'Board of Directors', to: 'https://carsonsvillage.org/about-us/board-of-directors/' }]" :dropdownMinWidth="160" :nestedDropdownMinWidth="165")
| ABOUT US
//&& isLoggedIn")
- .flex.w-max.px-2(v-if="isNotSearch")
+ .flex.w-max.px-2.pt-2(v-if="isNotSearch")
input(class="border border-gray-300 py-2 px-4 rounded-lg focus:outline-none focus:border-black-500"
type="search" placeholder=" " v-model="searchQuery" v-on:keyup.enter="onEnter" style="height: 40px")
NuxtLink.inline(:to="`/Search/?search=${searchQuery}&isPageList=0`")
diff --git a/components/DropdownMenu.vue b/components/DropdownMenu.vue
index 050d9525..0ae92f07 100644
--- a/components/DropdownMenu.vue
+++ b/components/DropdownMenu.vue
@@ -1,115 +1,82 @@
-
-
-
+div.dropdown(@mouseleave="hideDropdown" @mouseover="showDropdown")
+ NuxtLink.items-center.pt-5.px-2.py-2.text-sm.font-medium.rounded-md.cursor-pointer.relative.no-border.text-gray-999(
+ v-if="to && !submenus"
+ class="buttonClasses"
+ to="to"
+ target="blank"
active-class="active"
- @mouseover="showDropdown"
- @mouseleave="hideDropdown"
- >
-
-
- ⌄
-
-
-
-
-
-
-
-
-
-
+ target="blank"
+ )
+ | {{ nestedSubmenu.title }}
-
+
button.type-button.my-4.bg-orange-999.text-white.px-4.py-2.rounded-full.w-32.grow-0(type="button" @click="getAuthRequestUrl") Authorize
button.type-button.my-4.bg-orange-999.text-white.px-4.py-2.rounded-full.w-32.grow-0(type="button" @click="refreshToken") Refresh
+div(v-if="successMessage") Success! The token operation was completed!
+.col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
+ CVInput(v-model='data.email' placeholder="required" required)
+.col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
+ CVInput(v-model='data.first_name' placeholder="required" required)
+.col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
+ CVInput(v-model='data.last_name' placeholder="required" required)
+button.type-button.my-4.bg-orange-999.text-white.px-4.py-2.rounded-full.w-32.grow-0(type="button" @click="submit") Submit
+//successMessage = false;
diff --git a/server/api/email.post.ts b/server/api/email.post.ts
index 6bee0299..2d53c5be 100644
--- a/server/api/email.post.ts
+++ b/server/api/email.post.ts
@@ -1,5 +1,32 @@
-import { PrismaClient } from "@prisma/client"
-import { SESClient, SendEmailCommand } from "@aws-sdk/client-ses"
-const prisma = new PrismaClient()
-const sesClient = new SESClient({ region: "us-east-1" });
+export default defineEventHandler(async event => {
+ const body = await readBody(event);
+ const { email, first_name, last_name } = body;
+ const token = await event.context.client.CC_Token.findUnique({})
+
+ console.log(token.token, "test")
+
+ const response = await fetch(`https://api.cc.email/v3/contacts`, {
+ method: 'POST',
+ body: {
+ email_address: email,
+ first_name: first_name,
+ last_name: last_name,
+ list_memberships: [
+ "07936f78-662a-11eb-af0a-fa163e56c9b0"
+ ]
+ },
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': `Bearer ${token.token}`
+ },
+
+ })
+ console.log("test")
+
+ const respBody = await response.json()
+
+ console.log(respBody)
+
+ await sendRedirect(event, `/EmailList?success=3`)
+})
diff --git a/server/api/refresh_token.ts b/server/api/refresh_token.ts
index 14023184..28f45154 100644
--- a/server/api/refresh_token.ts
+++ b/server/api/refresh_token.ts
@@ -40,6 +40,6 @@ export default defineEventHandler(async event => {
}
})
- await sendRedirect(event, `/EmailList?success=1`)
+ await sendRedirect(event, `/EmailList?success=2`)
})
\ No newline at end of file
diff --git a/server/api/token.get.ts b/server/api/token.get.ts
new file mode 100644
index 00000000..66d6500c
--- /dev/null
+++ b/server/api/token.get.ts
@@ -0,0 +1,14 @@
+import { PrismaClient } from "@prisma/client"
+const prisma = new PrismaClient()
+
+export default defineEventHandler(async event => {{
+ const queryRes = prisma.CC_Token.findUnique({
+ include: {
+ token: true
+ }
+ });
+ return queryRes;
+ } else {
+ return await sendRedirect(event, `/EmailList?success=1`)
+ }
+ })
\ No newline at end of file
From 883e16bcf5340f4dbc5693f13446d0edcf6bca0e Mon Sep 17 00:00:00 2001
From: Alonso Toji
Date: Mon, 29 Jul 2024 14:28:01 -0500
Subject: [PATCH 21/47] added reply date
---
components/CVReplySystem.vue | 2 ++
.../20240729192419_reply_date/migration.sql | 8 ++++++++
prisma/schema.prisma | 15 ++++++++-------
types.d.ts | 1 +
4 files changed, 19 insertions(+), 7 deletions(-)
create mode 100644 prisma/migrations/20240729192419_reply_date/migration.sql
diff --git a/components/CVReplySystem.vue b/components/CVReplySystem.vue
index c4b50bbc..cedb4200 100644
--- a/components/CVReplySystem.vue
+++ b/components/CVReplySystem.vue
@@ -18,6 +18,7 @@ const replyData = ref>({
reply:"",
pageCuid: props.pageCuid,
familyCuid: props.familyCuid,
+ date: undefined
})
const clearSuccessMessage = () => {
@@ -28,6 +29,7 @@ const clearSuccessMessage = () => {
const successMessage = ref("");
const submitComment = async () => {
+ replyData.value.date = new Date()
const response = await $fetch('/api/replies', { // look at nuxt documentation for $fetch
method: 'POST',
body: {
diff --git a/prisma/migrations/20240729192419_reply_date/migration.sql b/prisma/migrations/20240729192419_reply_date/migration.sql
new file mode 100644
index 00000000..cd906540
--- /dev/null
+++ b/prisma/migrations/20240729192419_reply_date/migration.sql
@@ -0,0 +1,8 @@
+/*
+ Warnings:
+
+ - Added the required column `date` to the `Reply` table without a default value. This is not possible if the table is not empty.
+
+*/
+-- AlterTable
+ALTER TABLE "Reply" ADD COLUMN "date" TIMESTAMP(3) NOT NULL;
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 78345676..8c7233da 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -108,13 +108,14 @@ model PageDonation {
}
model Reply {
- cuid String @id @default(cuid())
- pageCuid String @map("page_cuid")
- familyCuid String @map("family_cuid")
- reply String @default("")
- name String? @default("")
- Page Page @relation(fields: [pageCuid], references: cuid)
- suspended Boolean @default(false)
+ cuid String @id @default(cuid())
+ pageCuid String @map("page_cuid")
+ familyCuid String @map("family_cuid")
+ reply String @default("")
+ name String? @default("")
+ Page Page @relation(fields: [pageCuid], references: cuid)
+ suspended Boolean @default(false)
+ date DateTime
}
model Image {
diff --git a/types.d.ts b/types.d.ts
index 7f8419ac..f73347ad 100644
--- a/types.d.ts
+++ b/types.d.ts
@@ -155,4 +155,5 @@ export type Reply = {
name: string
reply: string
suspended: boolean
+ date: Date
}
From 3b01e4cf02b7c851b725d88d13bba4b9ac1a8731 Mon Sep 17 00:00:00 2001
From: Isi Emordi
Date: Tue, 30 Jul 2024 18:42:31 -0500
Subject: [PATCH 22/47] working on adding to email list function
---
pages/EmailList.vue | 23 ++++++++++++++---------
server/api/email.post.ts | 22 +++++++++++++---------
server/api/token.get.ts | 14 --------------
3 files changed, 27 insertions(+), 32 deletions(-)
delete mode 100644 server/api/token.get.ts
diff --git a/pages/EmailList.vue b/pages/EmailList.vue
index 109a20a0..ac442fe0 100644
--- a/pages/EmailList.vue
+++ b/pages/EmailList.vue
@@ -13,15 +13,12 @@ const refreshToken = () => {
const submit = async () => {
try {
- const response = await fetch('/api/email', {
+ const response = await $fetch('/api/email', {
method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify(data)
- });
+ body: {...data.value }
+ });
- if (response.ok) {
+ if (response) {
successMessage.value = true;
} else {
console.error('Failed to submit data:', await response.text());
@@ -39,11 +36,19 @@ const checkSuccessParam = () => {
}
}
-const data = {
+
+type EmailListUser = {
+ email: string;
+ first_name: string;
+ last_name: string;
+}
+
+const data = ref({
email: '',
first_name: '',
last_name: ''
-};
+});
+
onMounted(() => {
checkSuccessParam();
diff --git a/server/api/email.post.ts b/server/api/email.post.ts
index 2d53c5be..9604c71f 100644
--- a/server/api/email.post.ts
+++ b/server/api/email.post.ts
@@ -2,20 +2,24 @@ export default defineEventHandler(async event => {
const body = await readBody(event);
const { email, first_name, last_name } = body;
- const token = await event.context.client.CC_Token.findUnique({})
+ const token = await event.context.client.CC_Token.findUnique({
+ where: {
+ cuid: "0"
+ }
+ })
console.log(token.token, "test")
- const response = await fetch(`https://api.cc.email/v3/contacts`, {
+ const response = await fetch(`https://api.cc.email/v3/contacts/sign_up_form`, {
method: 'POST',
- body: {
- email_address: email,
- first_name: first_name,
- last_name: last_name,
- list_memberships: [
- "07936f78-662a-11eb-af0a-fa163e56c9b0"
+ body: JSON.stringify({
+ "email_address": email,
+ "first_name": first_name,
+ "last_name": last_name,
+ "list_memberships": [
+ "07936f78-662a-11eb-af0a-fa163e56c9b0"
]
- },
+ }),
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token.token}`
diff --git a/server/api/token.get.ts b/server/api/token.get.ts
deleted file mode 100644
index 66d6500c..00000000
--- a/server/api/token.get.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { PrismaClient } from "@prisma/client"
-const prisma = new PrismaClient()
-
-export default defineEventHandler(async event => {{
- const queryRes = prisma.CC_Token.findUnique({
- include: {
- token: true
- }
- });
- return queryRes;
- } else {
- return await sendRedirect(event, `/EmailList?success=1`)
- }
- })
\ No newline at end of file
From 3252a0b51427bd3341af1a9be5e743ae3756bc13 Mon Sep 17 00:00:00 2001
From: Isi Emordi
Date: Sat, 3 Aug 2024 12:52:15 -0500
Subject: [PATCH 23/47] adding to email list apis working
---
components/CVFooter.vue | 53 ++++++++++++++++--
pages/EmailList.vue | 55 +++++--------------
.../api/{email.post.ts => email_list.post.ts} | 13 ++---
3 files changed, 67 insertions(+), 54 deletions(-)
rename server/api/{email.post.ts => email_list.post.ts} (78%)
diff --git a/components/CVFooter.vue b/components/CVFooter.vue
index 516f7c0d..cd9d0fee 100644
--- a/components/CVFooter.vue
+++ b/components/CVFooter.vue
@@ -1,11 +1,48 @@
-
+
div
div.relative
img(src='/footer.png')
- div.absolute(style="margin-left: auto;margin-right: auto; left: 0;right: 0;")
- LinkButton.absolute.bottom-4.mx-auto.text-md(class="transition duration-300 bg-orange-999 hover:bg-green-600 left-1/2 -translate-x-1/2 text-white" to='https://carsonsvillage.org/contact-us/') Contact Us
+ div.absolute(style="margin-left: auto; margin-right: auto; left: 0; right: 0; top: 20%; text-align: center; width: 100%;")
+ div(style="display: flex; justify-content: space-between; width: 98%;")
+ .col-md-8.mx-9(style="flex: 1; margin-right: 10px;")
+ CVInput(v-model='data.first_name' placeholder="First Name*" required)
+ .col-md-8.mx-9(style="flex: 1; margin-right: 10px;")
+ CVInput(v-model='data.last_name' placeholder="Last Name*" required)
+ .col-md-8.mx-9(style="width: 46.1%; margin-top: 10px;")
+ CVInput(v-model='data.email' placeholder="Email*" required)
+ button.type-button.my-4.bg-orange-999.text-white.px-4.py-2.rounded-full.w-32.grow-0(type="button" @click="submit") Submit
+ LinkButton.absolute.bottom-4.mx-auto.text-md(class="transition duration-300 bg-orange-999 hover:bg-green-600 left-1/2 -translate-x-1/2 text-white" to='https://carsonsvillage.org/contact-us/') Contact Us
div.bg-black.flex(style="color:gray; font-weight: 700; justify-content:center; align-items: center; height: 100px;")
label COPYRIGHT {{ (new Date()).toLocaleDateString('en', {year:'numeric'}) }} CARSON'S VILLAGE |
.col
@@ -14,4 +51,12 @@ div
NuxtLink(to='https://carsonsvillage.org/carsons-village-non-discriminatory-statement/') CARSON’S VILLAGE NON-DISCRIMINATORY STATEMENT
-
+
diff --git a/pages/EmailList.vue b/pages/EmailList.vue
index ac442fe0..ab12ad40 100644
--- a/pages/EmailList.vue
+++ b/pages/EmailList.vue
@@ -1,7 +1,7 @@
+div(v-if="successMessage") {{ successMessage }}
button.type-button.my-4.bg-orange-999.text-white.px-4.py-2.rounded-full.w-32.grow-0(type="button" @click="getAuthRequestUrl") Authorize
button.type-button.my-4.bg-orange-999.text-white.px-4.py-2.rounded-full.w-32.grow-0(type="button" @click="refreshToken") Refresh
-div(v-if="successMessage") Success! The token operation was completed!
-.col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
- CVInput(v-model='data.email' placeholder="required" required)
-.col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
- CVInput(v-model='data.first_name' placeholder="required" required)
-.col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
- CVInput(v-model='data.last_name' placeholder="required" required)
-button.type-button.my-4.bg-orange-999.text-white.px-4.py-2.rounded-full.w-32.grow-0(type="button" @click="submit") Submit
-//successMessage = false;
diff --git a/server/api/email.post.ts b/server/api/email_list.post.ts
similarity index 78%
rename from server/api/email.post.ts
rename to server/api/email_list.post.ts
index 9604c71f..14a8a1ec 100644
--- a/server/api/email.post.ts
+++ b/server/api/email_list.post.ts
@@ -7,8 +7,6 @@ export default defineEventHandler(async event => {
cuid: "0"
}
})
-
- console.log(token.token, "test")
const response = await fetch(`https://api.cc.email/v3/contacts/sign_up_form`, {
method: 'POST',
@@ -17,20 +15,19 @@ export default defineEventHandler(async event => {
"first_name": first_name,
"last_name": last_name,
"list_memberships": [
- "07936f78-662a-11eb-af0a-fa163e56c9b0"
+ "5992e572-a870-11ec-ba34-fa163e00700e"
]
}),
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token.token}`
},
-
})
- console.log("test")
const respBody = await response.json()
-
- console.log(respBody)
+ console.log("RESPONSE", respBody)
- await sendRedirect(event, `/EmailList?success=3`)
+ return {
+ success:true
+ }
})
From bc2cff2f80c0fac3c18e89d7a96a81f54507e172 Mon Sep 17 00:00:00 2001
From: Isi Emordi
Date: Sun, 4 Aug 2024 15:22:13 -0500
Subject: [PATCH 24/47] updating ui for email list adding
---
components/CVFooter.vue | 56 +++++++++++++++++++++++--------------
public/join_email_list.png | Bin 0 -> 68920 bytes
2 files changed, 35 insertions(+), 21 deletions(-)
create mode 100644 public/join_email_list.png
diff --git a/components/CVFooter.vue b/components/CVFooter.vue
index cd9d0fee..6b604abb 100644
--- a/components/CVFooter.vue
+++ b/components/CVFooter.vue
@@ -1,47 +1,61 @@
div
div.relative
img(src='/footer.png')
- div.absolute(style="margin-left: auto; margin-right: auto; left: 0; right: 0; top: 20%; text-align: center; width: 100%;")
- div(style="display: flex; justify-content: space-between; width: 98%;")
- .col-md-8.mx-9(style="flex: 1; margin-right: 10px;")
- CVInput(v-model='data.first_name' placeholder="First Name*" required)
- .col-md-8.mx-9(style="flex: 1; margin-right: 10px;")
- CVInput(v-model='data.last_name' placeholder="Last Name*" required)
- .col-md-8.mx-9(style="width: 46.1%; margin-top: 10px;")
- CVInput(v-model='data.email' placeholder="Email*" required)
- button.type-button.my-4.bg-orange-999.text-white.px-4.py-2.rounded-full.w-32.grow-0(type="button" @click="submit") Submit
+ img(src='/join_email_list.png' style="position: absolute; top: 20%; left: 50%; transform: translate(-50%, -50%); width: 800px; height: auto; z-index: 2;")
+ div.absolute(style="position: absolute; margin-left: auto; margin-right: auto; left: 0; right: 0; top: 30%; text-align: center; width: 100%;")
+ if isSubmitted
+ div(style="display: flex; justify-content: space-between; width: 98%;")
+ .col-md-8.mx-9(style="flex: 1; margin-right: 10px;")
+ CVInput(v-model='data.first_name' placeholder="First Name*" required)
+ .col-md-8.mx-9(style="flex: 1; margin-right: 10px;")
+ CVInput(v-model='data.last_name' placeholder="Last Name*" required)
+ .col-md-8.mx-9(style="width: 46.1%; margin-top: 10px;")
+ CVInput(v-model='data.email' placeholder="Email*" required)
+ button.type-button.my-4.bg-orange-999.text-white.px-4.py-2.rounded-full.w-32.grow-0(type="button" @click="submit") Submit
+ else
+ div.absolute(style="top: 20%; left: 50%; transform: translate(-50%, -50%); text-align: center;")
+ div.text-xl.text-black.mt-4 Thank you for signing up!
+
LinkButton.absolute.bottom-4.mx-auto.text-md(class="transition duration-300 bg-orange-999 hover:bg-green-600 left-1/2 -translate-x-1/2 text-white" to='https://carsonsvillage.org/contact-us/') Contact Us
div.bg-black.flex(style="color:gray; font-weight: 700; justify-content:center; align-items: center; height: 100px;")
label COPYRIGHT {{ (new Date()).toLocaleDateString('en', {year:'numeric'}) }} CARSON'S VILLAGE |
diff --git a/public/join_email_list.png b/public/join_email_list.png
new file mode 100644
index 0000000000000000000000000000000000000000..5c91fa7def00af1090206891de4b3a0266c180b4
GIT binary patch
literal 68920
zcmd>lQ+s7WwC#><+jhscZKH#Z)v?WvZSUB&JM7qY(y^WF+`bRzp6`73ocjmv%c@nY
z=2~;se3(@=##m7*O43O1`0xM#07+IxLJa_b)ctoa3Jd-33a{<6@vi}MQIi$})J_wg
z0sy1{SqV`MPos-m7~h=VnO~OMojo|#9!phJ`i+borUV2KR~hJt;glTN2?+)=S69kO
zUGlqPKhpuSuy6w=(TZ*2+@-h)14Qz*^l2?;^Vl`5D{a$X+o=|I2jn#2lpfc24YAGF
z)6>2unVFV(nXgEYck-&s7I3>JjCC=jH!|FN(be{r@sB
zfCT@Ua6~SWlKekU{hw19U2}r}mw^)+?3ifUFMz6&*ndsie+K-&llm_?_W$+Ce@*lM
z=gHyyg+v-k3V0R=_;bAte?CLpH06`QlJRA)`R=g;a=u>QuOoIXg$cCZ8awH=bQ7Ku{RzcEjq~}*DlaR>1R`iAThsC7_jCzfV8DzK7!-`TqYe$BtNEgh0WaCs%BDDiD`+lY8N|I1_N1mxIk
z7>Wu`+
zoZVy$42K28Ze-Pb~p~dLm}lRBG0=
zz+}bof}_B4#gM^4XMV@IWi9oE_nEq}&0`pFXnEC?ocsi7iQncnsFu;=JIYrNkULs-kW^cis8l7e;M{MK=A
zAhE%Fgy?Bo?17KzZUJi$r#{U#Z5hHIN!$^ATMx>_z8U<1BSg}$39$bBGsPFX?{*UGD3{Z#
z5%Dz1?BYIqwOXO%34Rt)}?!%DXUKJ{Sb9YUK8b`&56cIcjf
zxNSt)O#6fCuPyA2h8AaCdVKx3d+MVE!qlvm2?>3WZx2+`mH{ti@g3Hg?s#j0X
z@G%$j@PgIBDD^xJOe}vro@YLmGiwdV1G~Me6{A!KpUaYA7t&N4J4;5?DVVM%k;%8;%I(K-%K;B;FJfL}GXZXpsDSzdU_?4hTx+$7
zSri!YJ1Lsw(cjTNM31xyK@l!2SY(ATMJKiz?;I?6F}w-S<%E8$~=
zid74{VM|pN@^CQ3XmCI+1X<{-TEB(MT%B&=ov0XKP93%M0IW*?Z4S_
z!F=ucHeScKbMPr-4RGTQFvx{P{39FppsY^XiVxgJPPAy2L3J|P1(Q;jDM&$-rvGFt
zJjKL%qX@&o*l}K^kHbCw0~i0?b$9~$1}+#oLrbm-iu)Tx)CRwoNRJqjW7Qmgh^e`)
ztJ!zRu4Cxu`*_GA|K*!6R-U@vvKR8EK>1*7ew_%N6jj9rl{@lqe7yd9vX47BES?v?
zH`NWG93XqJm$c;Ao^y3G{)df`-)_Ja)|oH~xz6VMgBvX28Dh(dW)nZ&}40`roUJ6X9kL
zCYg~~fqk-yxvTs}lQkAa_;#piJ+C>U`!9RM(TRn%jxcxUx13zZo<;@K*G#z(Jnj?a
zRj6p26)nTv%EvI)q~O>Pk{-^6y4U1MlW!c^m}^mX0%s!N^4~LDqcOyBOL(K_Lv>`_
z$#OxY_AL%oUFJ`t1vW-^rqE=R!l3?q|BgtchpoX-0eRgZ_4y7-hjlAItMwLRpHmh?
z1pxw-(|2EoHlW2p{uK^-p{6f&shKmRVQR~H3-7D47ITmNjPM<SxREy<<--~6DI!U7whNTt9CduI0;G&CBM<$3rCnH)y5wf)oO;2UXS=`
z8lp${CRi+S4zdTwa6IE?vev*G)q<~_7McsB`a_Y1d0
zjt>{r)0pjBi4tJ6)$3tcssiN=i?y;c6BT^mVrGO>h@0Qu)Q+D{zBUm)JLCA?nU!CF
zm@ChX&-RLu^bc}AcsPIwJnB$$zmC@X^;2VJ-70nsPXmG2dhhnrhxiH`D5&dTtHy<`
z!fC{S#lBIP*X@-vAke}E;)H#(?(Vbr+L3X>wap+C5xGu5ve+d-E?EOF=Gpk?W2NPH
z)h#F}-Pt_B&erAl6W}0-K)tX($%-7)aU=yRLeX0$X$wv4&@*L%1Atn-UVlbi$c;3vE2IPTR4boEzt*#GOe6s`Mwz_ii3^vVDGJ=
z!H|lYlgjP}Lg0=>;+n99iP$i)0SBHsjV#v>=^0-Y`mBzkgg|voGOr7ky^Uma{^`s#YZR-f6^nO2xF@dL!!ISrj~PpzZjj;
z4mR+2j4|Uwk3$sH^x|-)Bqgu0mlSrcx?T7Btr8@)*Y2NO`cRAZs3I-LF@d}HG>V^z
zzcnASzVC7Sl#u??;?UsiYxQTs`?a{s_^YA0bf?2}Dc)-0W$){ebaIY}zd5F^Wuhx!
z#wH=U3ThZat@t#v@ure{>W34mR$WSbL|pNCqB|xDa`PcjtT1(NV4A=yj!ygUpDnNC
z(i$4-LjgU#&KG&!JnEK!UqZPHZsm^z4TV>>BIE{&QHWIJMwo$tC9SYPNo}+tSYi_a
zQh?g|N9aKF)Ztiwp0M*^+~%9b=`Sp+L~tRdfVWr03#T1?hZ4s)fJEd8c9(3(V<89iQr>Mh#j6a&WbNq}EViftBG_>Pp*Scem@py78fy!iQvMlNX%a@5^>E
z42qv}!jlzQuG-uCQJ<)BEMxN@!=DJRt79mu{?B5h@PVvHZ!+pvZ*N%KAJ3~F&R^li
zM)(B8os1W!EI?8)x43`1JIgh@j*rqyNlD4fU!vsv*V{Ld?1`|3qp=S=IdYT?&Mmzg
zY}RMq`4u0DEVe7#|LRCoYp?eD!;`J>9{IoqhaLK^0=|qhY-A#ULKfgecq0H2I0hcc
zW1SD5fIK5V+6)#hYoh{)hTUBwVI#fF!9oQW;~FFXK?Nlt`bW+>ff6nhkpa#ZATl3F
zfoVsm-oWm=A!b;kT_r(GDwjNnn3hgo+i{5#
zzAo>h1gZOEH|N&OF@t}``-GTC+^S*_ObP`iA`*zJ25z=?IOTPCC-ocYtV+t{!g@i_j3^KEH)XI#D0L@S~I}cbl_zC`StAO@VSX!cb&QA^6j3zPbzA5Nr$?2
z&slQafc;r^)6S>!t~r5
z&JH6sYw$HRPZ*~WzmIiG)@zI_r0M9DvPr!hUi{YhQ}$;-g^{vZ!%l2{{M)K~^`9=wa|E4l8f5hXVRVn5wc#D-
zaABv;lbUVv{JKh+1|ZJQ*$R6m2If!RA(#5D1g@Nxu;o|{lSl?!p6)N>B#JyLUx7n#
z@(2mB0Srh^a6k+N=}$~Bz=$&@CKLs8G#{aeTp(p6SkfH-opEcrlIQYcOIK^YJ^uxc
zPRv?}2DWuI-&TJd&fz^*@~7l7D6x5v6bXR_=sXhjnUR~1e8h6-5j|yKV(i`e~LkYVJ(`rFxrTK2Q%|S+Hb>{
zb)OBCG@!igjjKE=Cz1HuOOMmEx_YZq992wZV3YZO7
zd~V(fF>mzbJ0m8q-?qChj-1;XIQfuZRnh>gLI5RbfX8_}5W7udPE9=F`}A!bnC0Yw
zwb_R5XpQ>F*WB>**o}mD+3lD72*|}*vZf~Mv&%S}2=}iM%ZM*E0JV_oh;Y0rv!mB)
zqGUAC_87-C7_<&DpckLfKewyyXT02Kd(OYn=#IhU!5_3<
z$Fp1tw4$06&!Nx&j7Jg+
z{~Za@`5%7#zG~z8)ParPEIL^b8FvXBq$Py~unM5?ICH3onW{Mj!vJnYlBy|L31ytI
z$0dW3u!u*Q0E9P5OJ6h1Z2UO5v12mqwRVA0Z`I8`VIjD3XmW9?*`3skI?gyXtwe;%
z36hQ6*J)){;Xe9~Pae%636@5YK*&L)FrlUn^g512`I154h_WB!&L|r_
zKv#!0O4wgD$I(P&t?e_GYAe9&&%gcVAh6p3MJ3LmK_A#t{=r?_DQ~2u$xR3am&Kp$k1R&uyt|BA;5u(r#tI8?
z`V9%t3+p?^l}VF+cYnWH$@hXC7H6n;6`KMCvn%Gj^H{soR2eu=Mr}bCdzDiJim_$E
zq|5KSi>26C=jb*JUc9S;DodW5ma{JxDZjU{p-nqwz-Z?81@wZkWcO|;TJ-H4ftxs~
zOoSZj|)+iFOya}>({Kx#D7#fkV*zJ!Y=m4f($s-gQJ$eWSjkojniIdzZ{yf0sPvek5TVO6GS
z`$z1~j*FX=$#wLv*{h#eTi-4lseF{T^YSlUM2WyeDpEivy2+t$%(EaR=JaMWPQLkz
zi#OK|KDKY)6iv|agGh1b)40v2S^ApV9oF$Y79WJ~Nz^kX_K6)w3+wiHig@HwMQ!dQ
zFw(YeF?@Pfk@tLsCOtQ}5UpBJ?Y0?nd|hUB5{~}b&Va-sBhmqJg=&;jIExBBD;fsQ
zB+`nVe>PVm^*?!c}=G=
zaHjH((S%8%L!-mUO#{Uh>aq!H7zH+q;WJ)%0I`gXOX{UF+B}9={DXGXU0<_Z*@M~g
zf+969gO{fqrPuNoH!`p$|Do0My5_(!@tq_ctNgu_-(xf{|Zg0kAhQ9
z6uikZDSxZx=SztKx;b@I(c$Q;ypYzD;Hi(&6O8m{e3O#Y^zU!k+XMD*c_FbbgLzkF
z?U6HM4FaN8vC==QN(Hr5Jk}OOC`y#3cjzu@`03WeEq_;yN*n`00%lkPK@bA%AaAH&0w9Lz>xwYEk#f?pX1zMw@w&_hX(dr2Sg8fHWLe1&8FR7bx^th+
zddJR;y%=!v--tDjY1793ePVUncDX*e9^FqriLrGilZE?IZy&1nKK$67#Inw@h?CtL
z6?$*aSq5APb;sUZnQ`)nFerVJKq+Z*mzUjxxob}vhOw5T%X7VU7cTiQy7&Z918P~W
ziN9CJ-8s+HThRKmsR2@h)hbR7R~f?IBjcxGjeOtZ9k>&*q=_(Oh^EeYCGX+`uwg($
zuC0bQQfQ$=mOY`QKDVKZ;TP_JO=r*pU_Hv~ms*siq|T>5@#2d7{xGGAv~z;3v%D$KE$x
zVt1f>&J2I;*3;K?hQ@by{@^~b0sN^GjW)+)gm=r@UgvJeY1iGXAVreK*w@KBAxy=J
zBBRIEQPg6~!%m(7dZa5^6239e^M<%rn~#|i+-mw!4SO^^97_8WUu)*=Lt{)QfE-FE
z3oPO6NkKI2A~tZv`0KUXvwBJv>X+2-h0zU+gWpb7rRTBQ#}eN0g5nZ_)TBQtE$WHc
zl(SxQY8=iM3ec6oS+5aXk`V0K%Y?sDA$7x1d{x|UKDqq|r9awik|;!AAOr8g0c)HV
zuoAMCP~-sMKk7votC5ef7$O7HBnS|J+n<0bj}`d3;fy{CpmqWP=K<(8fdnpbs^0gG=e?k*lM%vE
zh5KwEOk1xX3I<|z;XGaRZ#wJwM``t=5=;2B8tZAU9Ego6L#8h5(4L-jO0jKngF~!f
zTH0Hk-2c!50FX)1v`>?~^&O1ohC82W<@$`-&$D*N}=fCnG3i^CCR4E)7oRdVD4H4W62!Naa7=&(0)H_dXb&Q3^+6Iaw(L0fB
z@{S76a&ofmd(gwd)5wGcS}Dp{*EeG;Rez`?6`n()%xz{^xXnJInm
z8c*!2DRQ4+)|qo!c6qWgN&i_7Y+<~*pGI~g$%f;VN>`$m1sj5FnL};BJSLhu#`6C=
zR_7Knf130(duympyi~^W>CcOUV8}F5#5-@Au%`J@+f)D1GiclAC1#?_H@|XwMIf)9
zxQk?GH5`~6TF$YmM=L&ke7f`cj55mCm6t~>>3M$Nl$?0OjqacSD=4&la%$&ChJw+&+PQs@8iTKz-Mwluq@y^U-Af+o}Ymwt9@m4#wx*`t1$^r
zvn|p9)1;^3O7l0=~Ot8qB8E?iLp$u69NHWk_=B?-tuoln(PzGkG+pt~n#q{Btu*?#i;
zd`*Z?ta4di^}72m>__bXaR5K5*L;;
zaSx@-rC4;vG~gtWPYACCEDz`j38e)@z|vG$&r{cU%M72s4o5;NvywNDN9bt#2jH#W
zeT$4up$MmO3hNcF1I?Gg$vqG2BvAQOlNlEyBefc3-5~#ogBGwjbyBg{I8)&phlnY_
z3tL8=<GFxR2
zf-D;B{+MjGJM+q3C@EhSiAA1jkXsVM&kc7PB2C3<)+C_K={R~P#jy1*Ihop3$L^V0
zoEodFE6$1eaB2JwmaIZoFUoC~chJB+eHm+#PLj8m*xUzyKbgzUo=-to+-nFZb
zi1;FN5NL*G)K2sxi2^GRMb>8GXzSz@huanPRN@Y3b(x0{uhI$ef(AnF%8`
zN%*@hMXC3z4%h18L}Zcjq+v}NYnCHir7V4nZc~(Fp`ao}mbw@5h>{Nm)?Dis@Xu^I
zHg^^b^JRu}yT=$LA|=$KMqR1pvzbR5s?0$-ezV@km$%Z~LcLah|Ly^!oC>lizjv0D
zxaLpT`NRfN3SJQ9;NcSIghdq@@y%4J*nZ+?_s|8bKQMW7S?)fV$hSHQw@!68-S+4o
zR1CN21`~7PPkGE_{&buCThf>aaOi2UTYaWZVS}mfwX8dL3Z5r{}aJb_%%7ZoKEu^_pqlN2!p-ktM2I;pt0W3LXbndN3kr
zA?NO=cxPz{veX(6N`(kd#ducX67I=Zo>4Q##Zs+o-d0I3_u;(<{3_+ma_2Bg#=m6=
zR|qvnqt5unz>U4k^fJqK5)HEJqF>SMMLI|CanBDex7uGhqct16})VBnE=-q)-LK|~x&3gme+<1K*+DBg(Sqmz$Y
zbEJv*zYdF64mL8aH`0C1Yu&IsxUA)JYngq^%=mfH6puX;E-2~CS5rW1F`;X8x_o%j
zSHtj!NMPBsfLci4Cj*8u-cUw{mL8L%va#pVM%s9j($POv2K6gha#xL)?C{P8DP=Np
zuqbBQpk&K3dTTqp`$wx*tQ+!QhYpX*E{1CMKtcP9VsGp~vz@ZBnm!d3J$`-O>g}RN)RbWMZk*>aB?^04@
zOpLazNy?X0_h(Bz)8Vr(H}7HX@d6!@MkNNk<(g_0#r0eiLv**=tfF_T^`)NLcWq{T
zzIIExbVQs3NU|F}L)NzT2kyso1;_M}fv1(_Y_e(QtL?8fHMZQwD(m&@F}Dk+sf@SI
zx)!q^CRR>T->~Vx1f(fK5um#cGUoC&bObEku99gO5B9t~57gN)1S8U1g7U)T$b_P>
zam6s9Xi+C1%UC^4$+u}L6~4EOH(O8d+X@F-us3$~H-Xd&6pz*xbci3{u_2+oZNn6#
z7ywG0y2-^$S@k_7MY`Xc9VYAAixBc!w~X&NvpfO>_-D5s37}>+DEo|nDp$;7lls>`
z*MwBlQ=~-q1dyBU2mSnn1PN{y-}u)-M5PPrU@YZ2GSZ3|WqmP;i`VoH@8gnlEGZMN
zf5+b)u>|~VAp^b;!9xJL2z;<1mjhV@j=rz1ifL@$-2!$*mYg=W71JYOk|4j~1Omt%4WY6zQMnf=HmTi)<)<|4-JrNl?XCPPzgSIgAeu#>I^
zTIBOYM?VEtDy!j~A>?yxi_SKvv(GMQ;5~Ss#JfH!lzt0_#sOnSPZ)a8ST)jXoXN;u
zuwt$~hpNrYOop%_;CA@OU=bZ&{z#%y`j
zeozCI7W_&s$Ck!D2f@IG7j25$s@>Pi2!X3feGjE$p6)F&pU(3V`bBevvc0u=R1JYM
zU|_`(JYs}M2j1!D<-C0*b6SRTg>K>GG%VD}tpQe7AoKGJlfHN43?-WfXj=ux)?VWk
z)`MgnN(0yCp3g?KfPkQWcAHeObX7WgL8zsc0C&vvmJrz{v&0Zbt4to_0Q2`U1W`sc
z1^87CxIAgWIrToB37TRqT35Z;W-&}%gRgg94P~IKFC-yKs7q(k=;f#W%&Y0susGAYvQ8AE~X_ilY^N`V>wd`p^
zJT5$Z@gL#iNqXrpePupir5HL!x0Neg@#N6u^^wImrTk%Dh#R|2L5$J}yG+RPV{(ht
z475ZO%=qpfuya&V@2Janm`@82Tlq6rC~Uji`7N50Bqml{EgRu_^$;HUNtxgu$F}?I
zeW!sbx<`n>8jg{Ik5$Z-?bo@PV<=@gXH?@5eE9yu&CGjym37O-b*C|9v`1x}(&lkK
z4>_@39a)mg_zyOO
zZkmBBf>qI?lb1u9^bd#02&~dDDxYh0(mUr8pK4AIEFRDnoUSquSJZzLd~Y+vetP^;
zCA!rio59GK{wbp=VBz;@Z_zhH%x~pD-kG(nlMoFJqWZSTE-a3kp;zq8Kx)!qRV}uQ
z#;mr^YR~%Re3r_vJcuXXXiHmIqdaxB{e1eYMlobefk%7?&s6M;{VC7}noqwlAHkUk
z{8*lGH==2b@*>Bm+Mjo3pZVdGMGeLhtupMKR-h@WRv}o_z`Lz}sz8#qrQ&K=eJfu@
zMRZR5@axV$Xa
z$1OAYF5~*PPi0|y3&|&(imOj~6Z8H;R5O)gk-!67(#X(^0S)r9Z&Iw<*2x1|_blc$
zRZW#=-VNtdmOn8XX1m+V9QnpVX?|5$!HrQO!RPezfRTr)%gWe>%hZ4(3+ArA=@^hG
zWN0l5bIlPcAK)T1UU=TFMKoAhK_y1ULhITvd#UBB-?^TNL@Jnfs
zodZwQfVCsK_3y2==I3}8X
zvIul4vM^wvD+cs0S>_rwlhjUH`$>M2hE9JKP2M^e0c3yk_a6r;^6G|m!)=KFR`B2E
zC8!vK~@5zSQ3_z9AM4TdSI&_Oq&$
zU{3Iqsy<7jE?56d^H*4h7tVHntWU)
zRF-V~P8khcu1{}0vC9_tT9(@Nj_yE(QSH=>MnW_q5v)IuhNrH@DTxYHJD<)}X%kI7
z@SX2G@$>|2XdoJ
zk26A^FJx%$h)<3NlrFjfYP=$9s(+YO)U-=yiO3Te^rY=cxkksix(($n4&2I9GS
z!1F-=&fqggl^UhUW*Ov^u+UnVvP$M?ibEU~YM!Wt4wf=oSCza7-1ex$M>N-yME;f_4Y2^l?Ka=;IW$<`ZQVXQv3+!2Sk^v0`Zp>Zr8sdQAc+*Z)z`6K<*Y
zbwbYwr~jZ?-EWGtFWp%c&z;c>M(WeXOAJuDJX-oaCoL!Wq{+9k8KfpTV*I-Xo?AW5
z;|WNOGKgK=pK|HZ_vvf{{a6aLLLthQ@MNarh%&p7K~DJJBgI_7=hs0oYE9B+Zr!RH
z+%2-gwsR*8k+HGEXpZu91jt4s5xxko=Jn7g-?V69OGrhq%bSOaw(Cd#I$aedDLB}T
zd3bz7Un;}osA|+(Q8Mm+h>spmx7>(+Na83Luz#l+=@!@i?t6#MjKK_TCl%k6K+2;x
z?4+5cV!N4P)JDRME95qv^mGF1qQlYh?^HiThE3&AuGvf1jb&^Efy
zp94IqZcn71s;DRT_PJe`B($C5Gh$>yzj{{mmpllJnq<(Ds_Au8qzII7k{MN;@6#$K
zPwPI!t*5z^c;{p(lv^{M5}VvXeEP*ZiEpt>Qd>aE86q~NbDX4TZ5kR0)cDwGcDYPm
zTQdVYwp>g`@yUoQ$*htNmfpTg%dVY8sk5w65Xqhf+^fP+6f6lUc4>#0&jVFwDX@!A
zSy`MEtwnSGSbW1`Om4hmlt+iffCDbaV`hnmPPcRR?Ile~4-^i(-d_`}q(0=<)fl4*
zEH_j0db&1bA(`SEj9uYdvO&pU;HQi%b(eEVEJ2@#G^N#f>*+yGFmu`ax0<$BeWEkt
zXF#kzn3E;{!Qqt(A{_)oA@E3+CWtMC&~EvwWpC){2%D9;yzLh@z||cl6K*v$n7phu
zK+&VCC-Ts#$kq5v2TwlP*whM0!q9ce4`Xr>dD<>q4}1QVV@qcHC(g2xJKG+D-9Jh0
zy`TJ5WKI1V2$R?SrM11$2K@r_BVnFQ$z@M<-pa>mzmmX+mv?M_VYU3ml{2-m;RZZO
zQ9<1KOQL`Hr(}@oEqNfMQk0Dt4jG*yw;CNvaL%S7Uz4AHek})2={u?7&x1=J#mhQj
zHxeAUEk|qd^V3iIV=5c-iqtsM$XgO6pvheUX3q}
zIvp0jNa(^oz@@5S3&nI_iIC*pd^5C{7Wmfs-uS%K(t(NU!2h;(#mSgF4T1SvBo(xq
zGFjKRwIAwNZ{=V57&}_kE_3r#m^O2zb{8WjJ!h1ww-KiPa$`a(kh~`mLTZ|!X7RTo
z!~2v$MI1Elf5bgqUg0J;+g%am+UA&_f(Xfcgud4iIJIa&PDTL^4(EjCOgUkK--4rh
zHY8`JWF~5H`LyZPan;a&+F#q2n-{WC8~fl6k{h7JY8K1YqUU_44XdP@{`I>;p)^`C
z{Ex`46}+|+hczOp+G_>bC@8CS_Vm_E5JP+@sH$G0FL`rwvn*x&
zO%K$K#s6m%B4@3ksY3QqcSBn9_WKTJGzYu3%!nM6P$5mhwn#95Cs7KM6Otoq
zsQLF}6y40N)akx#Z-cMA+lBv86n{6-lt&}lmj+?IKq;Ffl9xqW*D=dFwVyH9STF)g
zNjxWuWJPGnIQqyXRo5j}a-R>`V4GS3loa_hY|oWsQe35pi)Y|eN8wxL5z5)Ult-iM
z$LUUQBH@!C1KUL=l^CRTmsvuq4so@9$FZ&z^Dm2#f^4ooa>&ib)5E?lfaIKhr|uv~
z*dblwz2HDvbm@%X$VSX9GOLHF{-kGX$)V=>3(l?u!EA&yscYq3Yxks6i;
zT104Hmw%#!P7a+pS;7u2A1NntwHy@POioRNHP}>$jTosbh>duY(cI*vQ-jiGUqWF~
z?{Y?Sy5oUuDOf+dM9FIR2*!c<%1%ESAI>29UL}yP$~Zmy*-`UXdiTPYZj+XRbno)@
zmBG-@Q(r=HHgIXfsJ~+cN_9hkegxe-hC`lZSTeBYr(i$1rq8(#x|MDV8(dABH0V;Z
zOMOR+q9G5U-{)f>TA)_l^@%KEXh#g&cqu39oeUJIhBImsBD7~Ysyd&G%z@LVkfYiZ
zLlnA?@E%gT-p=Q)wI;Pt*tEnVE&&uJ894HH(hyZKcy)3u>pPqk<2Ou2)pNPJ-p?3O
z>oTjNGw5PN-jfXt`I-E_lRqtbL
z`gS}#!gz4CO2{Zm!FC4nWY9^dQ5m%4q6v(EQHhvfa#@Q4t?T7X#`BcYoagRZYr_Mq
zZ}isLbdJ&rp|T*`_=VT_WWQ1r3jS#{xd|n>w@)2yJj`v+xXZDJvIb
zJ{y8ObgV2CqDjXIKGF!|mzHGZ(tz;}ee6N=MX3Y{!UY0fYf)Az3{|E$NU&Kuss+d``ofzLSuPgU{EDqf50ll58S}lZVL&JDqBf1iGN#Q$SaanfF
zn+N>i`8u}zX(L^?d-eXZChzOx;hTN?v{P-dP`odSFQ372B&6N_x74_z?9Y=-AO0pt
z$;)b|bt9t?L;~9YJ9Qq*24s3hp<>4ou*hhtezzk+;(KXzu{=oY%A6f*8}l541Tf(n
zVxjQUDDIf(d78<)OSYPo9giI&eN99ARfXO-5l2BJ1i=^lVe(qg5x5->Cb(Wp61-Yo
zlO@^eyqZqgY2w4aS%z(f*D-){w6be$fluB_zeCyP^YwhrrOa0qL&PAfiTZAHDZ`rR
z1V^Y`4vAJ-h`E8)P)-UiYA?f39miB#SrMPk*mX42@a=)4*C7(wRWbhkQ}{=?4-JMv
z7NrwMWb%y;b>0{)COrhIXyzE4(lD8V#%c0;(u6ct?S49g_U8Kh$NbQbU@U*)Q!8U7
zN--s10w4o-65g8~m#Ne#b&SrHEsP?dZy?J<`LeQObh-^neO~<(WVJ#y7kDzFVfDh-
zFY%~iLYdCWph6|nLk%2e2Zw~LA=a`|+?c8zq-jwaMSf4+NpR;a2r}$SAzjy9?m6$#
z9y4#pyde)_i1B+>{~@H9FhRq#vVBV8c@R~Uu+v5U_qPWt`DSxf#!A!9{Lm&Nn!$Nk
zRByg8$@{$-z?L+E3oc%ycR*INtbBn%BRHb6xmW4sK{Mll?QJ*c|M&Nf($PUsfKtna
zVIeB6UI~`KY?k|?f&Wq;64lU@g*@mqM!Pc{g##Zt{wkC3=D~N$M)fK4PZ~sc&Y+g&
z=L!(3x!NV`^jVt+eFclOu-9{w2-0P2PCA51S+AS*+6Rpy_$-L
z{{DG4bTC>Pm87|RyW{AwziquTPC$HdjCmJCNF7fRigK|~{!pRZ$D)6X6gho?q&WMQ8H0`C
zg@(f`6D2_XIL#<3SM>K-&e-b&+4}L`7c8y%a?^t(En`Q;`DKCoOFRd-)
z|DC%v$2Z$#^w{#aWIg*z#)GIwGKeQZ?Jq8Hs-f3&ohqK74i^dB(!ED_{cZ02*?h2f
z$&*^4sXcfBM^n(MrtiUP6v+J}gmI7jGt|uSAN9dTY=Focu8=4v`|@N|RrTWbnY!~A
zDk5K9Ry3{0vHJm%_?H4Wcs!ikR-oCQ+cSMUjIJI5w@u)TI??s@hphK7KhZalUk54q
z)O(34)CqIvntVwRRANa?=DH#!nr|g@nlB1UUQ7427CR}viF2uk@!Rfi{6hM9gtyz%
zP(A9T=_89AQLM1P5hsJ10c5j!-=KRcbR$q_AR(F-Dky(fXa@6*K6yS4O2*UMXuFzw
zxx3HT_;7Plc5kOD%m%OzP{sf(3TSW0F$-iMuuwg7P+189Eqn?Yf8G_aBm+t?pNI|&
zz0!JV2+U8nglC$r7u+bbXPW>aB4(T1uXk0~D6x=2?uK6w|9(n!JRhO}Y0jbTT>5@1
zpSS%B1`gw=K#zy21AR)@qATHPAq99s9G1*mri^_BvdluCyC89IIiqLt|A!V}Ng&TK
zY%Lq7s|_169NM47+uKrv+8z?#kZ^qhnO9D}FSXA!&d1Kuh-)lqaT{%Y$FmHVq=L#R
z$IR_Gu&4UsX~H`F3LmNQV}dNwL~&=E$W}yPZxNNUSj!dzPE-UhZB8^0mh=v`fw2G(
zP9Xvwr@^jbCSXU;?3Fe#`E?IS#leriUbuhyrH#^dt)EP6WmvK&hRgJ$QHbjoohbSO
zY4~Dv@O**_9#dcE;Tb_bWS*zFio)t=?o<_eFUa;)$LiZisWrGvx`ND|+I$}%glBLb
zmR#gK89vu9eNFcv+FI;Wf4W!Y@K9tuio!uHEbr1vvo%ky&>^cAek*X
zu7^F5FnzHo9TQxbQJ97&Ml8uF=78|-+|qCsn~>4fiRxL
zDGrbn2r+4rffN)m1y89>?bQwQ9?uZO#`E!$In?yd^@;wD2Q^Hb$WBTL
zWs)P5OoSDT2m*nEQkb2?pnxzEQvnv@F-oI(A2aFvah9uzMdGt4fd|S=2C4)W;t*l1
zbm@!M7@_TX&s%G!KT~ko4F`@(J;44q*G}*a^j;tKd%uIFlNPmt7y*EDXEL!tk!J~u
z&RCR6q>5RN*Fj2^hQ?gCrEtQ%4p-eCg#3TYalHbwOX
zm3LGu_(r32yHL|}zsSWH;ek{($Rgb?qzMzY(%tsT3k$Lw{L|=hA+Gn>B54hfx4Jyg
z=Gstw)zT9|mcBpU`^{9q^R4*sz$`>SfLk3t%4~gb(yCjY1_vXFNGjxPKiYavI@EWS
zm>S6^pb(*ynnPSB6NMbd(->yJ31UJj6SFe}hOt`HFf=SJOLt#Q|Fc(
zQ0R?6tGI0WzN6dTo|y`Gf0qvW^3oWA(MIcsg%cYd&zn8xBM?{M?b~1cTFl!g8RQ(2
z&p<>Q7`3h7_oZ{Leo{Z<($3=zh-i82UvFW9-f#26gY`t_|MT|VVNz6i+wiGe)j5ah
zo}6b$L(W0Tpa=>E5XqS1y6Q%EO>08MbytFdAS$S2MahbQgaKxTnPHfmdOGLs%2n@I
zgY1L8yTZQTbv^ZO*Id(m>eM;EbN}uj0u;pniy|V8fHaH1Q2~`W5=-7D2~V07iIw+2FNmn`?_KaAK^d>$dLp
z#)mOBN#Q7&CiJ#5y6p0gGo5`t1=FrL@b`i&>tAz8a`Gkia@ABM8L{;mvtSyT1~AVc
za#Vzb6e@Cn1iY4j5F;c)2rFSg(3m>RL&j_oc=4US_dM?F1wQs}F~SHpqV3$NSHyUz
zL`o-cNECSlA>tT;L^+WYd08MZ1kn%@Y@pLTj8ZzFwm2#s<$d3Q^Ky6C`%O-RL+c8g
zsw?OF+gpB*@;oAv+Bj^mtnE8*>09cO(H`Y{*Y0@r&UjtTGTGB{vq@ox2@+sQy?~f9
zwiisl^JUFX@!Iu!){W^izL0g>bRpvS{t;ye6jL#v(rKIZHv87>2@}>Bb(R3{X)d~W^zg%cFmwl+P8w(l0dQW)VZ`Emcj%($
z_FUNbwpKu;$Kv%DXOMKHm=cp(i3>phMdUQ94JHsxoWXTrtvk0`V>SJ4#N3LutA)pZ
zTBCf&cjWty9e#ofdGF*C0SiW9a#|GPj0_?G#%Y2eF_C6%6oM)^5+b2Q0JqqWSPKiD
z%gP?pE7?#$7Czu9>^!w+E1C`$0A4~-m9e8>*w_yw3|Vivb$GCT?e^)3Xz(e2OT%c2
zq9mi$_LVs+=cDW?w|)z(Wh{H{X!hCf_AgBYI`5R|po`S%m;`C_q6Wvh^6`^D&MhD6
zRpwRTz`Fh&HD?}71_Jl6@q}7UX=D~aQBj6th=i~xCnIVMw}}F+<^hk08kNs7p#KhB
zuYIFnnxpMTyp|N*^V#pV+$J{Yrri0Z@qo
z&>{iwb02|Tw!jyk-4yOYEKZ?j?R$J})#etsAds4|HY|G6(YjC;O6it}47obE8BoWnxBVVnEd)nuU
zOsW*d5IzW?lr6t>gWX-cwRq}Xy-97#+Sa&x&CR~X>J@arGfPgyFde3rU}GHJL?wa;DzZezil{v-$PK{)WmGOvE=FmKh+U
zI7mSnQ9y{KEJF!I78nJ4(E-r}O9GV=1t=mzoUyduaok{eksLJV2Zc?l$Wf(8$1RbT
z_K75fv=1FNY#Y^oSg)bHeCY@6+5Ow+GLgXVplDaV2qUc8k@K0QeC(#|!BbCzSd8!K
zs_W~mJhU_w^4-QUDI<-lXkc)(;Rfsbljg460@-Xglup!p*r}~mS035)B=7IMR}OU*
zC@dV#H
z8nGkW3ffNZdxQ@4ER$nCN+317D!cRz^Ptfm=Zsr;1!O7Kmo|L4`DG~{ng#?GWl$wu
zXy`Q{%YGHlaph-rZ&?ds;l6<9VM(p;%$WQ?AWO~HpXGDG?z=+O-#-U2X%(S0b!iihIAc-MJEe3epOhTvv@MAC?MPyV4G(`xc#rl5n#A$!m4Vl*q
zvP6!5ncY~Gx|=wp!m@7tyTTcojCFsn~YA9Bvun+SX!G|a9O3dts35E)SerW
zF)4@1rp^;tfsV#^1W)rwDV;JRgg&9o>UY7Om3P0Ra9Go|OcsXKEuqhUw?dA1`vX}L
zIKAC($uC}7GV%(@()Q&`PFa?hcb)xV1(Zm-aAhY#P{1OnM2kWMM&JYrBRnQUm>M7!
zNC1=|0ffgWS~i+5Tl@8U63Vhy<`16N+i?4}t7iKrlTs~Jck#Z)DMpa6ONk%?01WY{
zhD|`)ID_b8GO3HHOs2JCXUzDb&EFsiae1b<=G1T4sIMH*DTG8x0l-iY;8=p?IZ+T;
z9780%EXzibSBOFenlwAlItPq+iRG9R#kVd^-Y9Sm_U*%c$>t0>8Jp35>D+@19WN4<
z#*#+w$>=xcRYXPZEWYJ|pUKp&_nUtWWcgv^%ZsF*wiUqJK1=~w5Mx$7VYa=||K|DI
zu13ns=Z_ewPaa#84ttlT{GEL;SWp=Cw1_|v5lAtX7bF6LF%>UaP)X8>5QNegCYbWd
zj%uuqw+qWAABFn&PxUfc8qS&
z_|8w>MWVim1QRo0vIKbL9E6axAjwftmJ>LEK`4ycM4nf}l87qXeiFtbnM1}ut#-S=
z$+xA&i|b6`@QJIUs$gsRuOy6J^*8D|y>q3nsdiC18mN#&9$=)J=TVK1
zm*KR;u`nTXY7#@NqEb^J1dr&fC$oo5e%)nq9|j{vFrkwtGlFOL+@5T`_$rwS60#&i
zB1`}dQ}HQO9TQ~`QDZW|Bw7zR+K9=r;$H;PvdpIdEQ>fHkp_D-7LF!Vk_77@phHF8
z1Pct3Mo56V^L|tp^x0TuDf&QR{?yji;^Oz8-$RGJ4<&tFg9QkPW|wQ9(Vej|Z_1DZ
zvAPHn?{2xRqq^n+Bu!Tk5`@M%m~#yp{b!vk|8x8F+09q#0su99{--70>dF;TEIw2X
zkpiT#NA%gnD~cvgJEBA@|67p7UhrQtSuXtZFH@v=@Cm-7eu@Z0AXzg#ur+hji19OC
zzq)6(yz|t(=}7m=RG@2=gu(!}IJ~&Y_GaF#_k1XP*CGg%G^_sjk*8%k+()GXQJu|t
z+SzCDAN2P8MgWX_d;PJJcsQ^!&{{JaC1G^}!?+}=Z73c*=ATKP*{$Yv1EUQW?&P|f
zrW&NAla!K*Fb3ifoJ*jZBuyE@e6L(c|Ne*>vp&C;IrRU!rd<8I{}qr$p$F>Xaf_mN
z(TidLl$E8M_PvWGePPOk0~0J`#mR8su9&xLjF3vWVM)dy2m%t%0vzU*ep@fK2Lc#?
zFcR<(1~?f43UrKXD2~z_zEc@ZyK$rWc*L9RwmK@3ZU8ticdi_5yP$(<)=Kk~$KLPf
zy0l*#Jmn>9-oL@kr(+93KkR!9;}|=Hs90WOJvZ|9dmjW8-Q3<592}|oacQdk@;wBG
zLaZhG6l}1r&NAwJiD2-dKug0+Okgmz&RC^!WNpr!H~*i223X~nt6veq{@a8U?SxP*
zW6JHjhfo>bfQJkZ^!)h4ZQ;hc7jcOrA+^2}(HS-j95wM>-H0i@F3ib8TS}YHomi6e
zcRfThF*3=+ves3&E`R)-HHNWM&;3+bBXu7AGk<)I+LN70HI?`K&)Y1LI|cR+cThqZ&8c*Y+JhZX@W2vy~rTusS4ea=4}Hp18Ml
z5{Ur_R+&7w-n?3sRk*XUITmr`^-J};_uijr{(d>ON-=lx^A8N!c&fx-b$l5U_TG*0
zF;d1TQJ-1(4^?K#j;=W0h$AMpBDZe@XyW;mNA@p|27M3UEUgm7-iXEymD&82yRhKX
zo5M)`22Iwlx``ZndF~BO&2FA5-9{hBvP&Gw?624V1C9&lcQz%6_-J+}LD
z<5QR*7YDzdhNFr&G6>d>sXulo%mmC`2#8Nw`%Ps(S`TV)u0
z>p7)RJaul{Z$xj$y|B+ysufrOLoi4|B!EoL1Fq3i-*!yBcc0SSIlu9RMSNH1a;dv>
zxZ-_;Waoa3SS)|B%xf~AiFS4k99R>D8j9+;VqgrAOfw~v5;ESc>Y+j2_EKj=^w
z9#u@F6|bP$Z+0yegZ|}$-#0*naKLBeor6vG)x#fna2xo`2W(BDO>d{9lCEJmc>H=aZ)Huo{Ke{}$*RhwVyI^*5LrmjYB_`3
zy%p0NwsR6yO99>_^X!svYx6BaDv=>6Mps&Wch1Q14{P%BkL$~)hUF8TRCnk?QCH=z
zty08aAhA4xsf-;(qb7W)Fw%sA5~gYHq|7>^RhjDxaTTUj=7p(2Zv6;sN_i#whOw_To}4*2@w2(Flc
z(>ho72L@->9)_jcG=MTBz5Yki-j16&I+h{e5DPi2-H!Z%2MPua`VlI;fi$>FKWHnz
z-7$xW242Vf?Pi6d0hygX*pa<+@T@t%2d<$?0@|-qrj?@O-xdD~fOGuF-Um}5|I$>*
zTaJ*p>W+UgygGmeQIxGHU(#cnW58xo(cuv@J^o
zJtGht7mZmt8&wwbhKvOhPb(&TN>W(t#F*UrlLuGG0y|o(qZ)Kh%Mpk$o?r`YU4@0y
z*5Sb9goaSry`|#
zXVyVfZ~6gO>8so~%~iaO{-b&CzuB9hx-DNK7OUd{2ab{ebrc>z&Gx@n&T)
zxmF5?`pP^9OB!8g#-zE=P$umG=b*~)w;Oe%bY!tTVSFdh`0BOCEm@81v$w?xgYL{vFS8;m*6IShovN
z@MX0vuC+M7FlD&5C*sjdl-@uIetu!xur20kuRgLm?p49
zR(b!&GfVq?s~I})U%p$vx@XrPG`H7Qjqf^$xmhZEDT^(~==1wm
zKrw$m1c7-m5^=TQAy5hJ6-Fp<83DNb3n>tZZ^Ff9_;WUcATix)kjk0i_crSUIB*hVIyXR8n0t{l!0wQ3NiuB`F=V3>7zdsZdjqbdW(1ja0Bq{aj@sD0v%=a`emwGye
z5fB6rb9Oypvc6FaL;WTl*M>R1ccdsUS+m@ZnS0W3xqsT
zhtY10ceX8}gMn#GDwYeOup9?O+K^fFcJ7ENA6rIFxpWm|Y5#oHANg2dzL<=e71nM>
z`H+oTSN?}q_s}1~poE;NQ(I2$+W0OK@l3?iQ3DDjz#=3tWVny0jpk1{lsF~{tXk%U
zmBIG*86XvNp|S)dT$48TAM=*@te|EkM|9}r(*~X1U*0`
zj)vv-`~m&f%c$!6h`}P}dNbFU<=kg$!=Tx}H5W#JkhYRlg?St2XENx%D<-{R)Nymv}_si*&8^H(`=*>0x
z<7X{Ola`jukt6BaovX{3xPMu^`sDo@j-gOV1`
zh>xEzLHtiCf4#r8v5{bDuj|sT58mTK-jNbd>mZ6`5RJJ-@5)|94u3nANS-%Qh%VUFa!b4IAHH_~%Pg#H9ZTwxfEA
zLX40sZVzm8Z|XaC*6aG8jBlIXdwL-o>RyrZcaGv@3E&x7KEmdBGk4luANu!vk4QrL
z4~e!*b3{7oqEu*OW}p5a>D`X^!OagT=VBS`->+&R?MGsquQz9HVIByBbCcLGPQG7BkMRo3vyn){eYcqJbSXLCRTn9W{K
znCY7~(Fv&({p^gg
z!i)QNKgNW-O9(bX2nZ?bbIV?{51Fvpb+1RGr!
z5limJ6?Z)PE^s>ktr6$7bKokI<;chD-bCX5S!6n_!eK}>IJ1wjyey`p@k~CQ&J_>{
zgDi$iq|vm|>dxKnzWwRupP5PhBp0I7@fU=qs%eO_FhQs!pxS^dzw}Lw#k$v=`
z0u6e)i@GmgeoYGc2FWb1mq=|ya1fdI4yBXTKj8?rK~u#RCpr;BZW>
zZLzrX_v#(_YjKsK(_|~50ak~H8~oW3f9UQ|d-HvADv>EklE9Ig4%bc7pHpYp4my{1ks3Z(7@zLN(ZLZX2RO5%Z{24f%sa4JI#HCVTe
zo;v4m(2$8OZ3i~a==px%<6=B~Glt_#TJN}Iu)CfaFml4l=7#1RKIC5>sy@CP6KRTr
zF(9jrjqakdjRvdzW1|-L140e8U#zO2ld-4LU5%4jj?-|2HX-ZnhsQ5m`2fgH1eAm9
z$xUx%(CK(_+}~k@L?D@L=Jwoz;x_k`W&hv8{yYT0IJ{%+7YkF~?&V2e&tOscuEv~J
z+~|0F_-*q(Qn~@X9h1{v7oPoQ+oNoxXNgKmQVc*uLvGOloI`PT+S3!5jOeh}7gfJvYjh!btDJXQNsPoLu&)|5VKvohZNJB^du<;)fD$RPTXgK@h
z?oA8XNbor*7P8=wDCjMQuPj-vkFA4;9|A}^1M(d&J9rY#`Y
zjB%xdq=0I{hV%CJUH^PT2zsZ9iI@SP7_HAP+CfJ_lk-gJ7ZE@T6)@!->tp4<_jd!F%-ZwSTfUY-3+3adX-1*xE
zjhgTxC>#I(ylEsbBHbE%%T
zySR9`7=jRi*IIfsj;yDOXWacQ=%Uk2bd96Cp>Y-5(=r}QC#?vK36jbhFlQC~$?SIT
zH{P;Bk>g#B`vsT&@yBI!pmP}$^b93nSw;+|Q#wb+2f3pre*<(>u-6!}tqnW7>*H)$
zOqw-%DxQ_&3aGPPQBd#`vu@iqJnh8#Rvy^(5|j#$$M`hC3V<=^_xV_Bwti;Rxi4sE
zm&ZX}3q-ejZS5^h^Js7Tom|8}6oFBYq#PlQEo)Wzlv_8e`i|?Bf_8keYH^_9!U`_p
z9S#H<&@c)#*~OpeGxD|+l@*=Krh3lXL8CKLc4rMJ^<;sN%_z)93#NSS?qfKm;nC(PJqtYc0NIr6OJJO&;lff6Z-l$oEjZyK}4rV}Mhe
zqOj5WLEk%jujz~neMFP+MBatpovC&
zr<#kpI$D>B@!;)nvd1OK4B#|+iL^TYSv-8)yV_9;E(1_WRld3Lk8-$Yt{e~NL!!Xz
z9NCpBOUB<#`F#(oETutUP9W`-=SpMswU0BA;B+CKvH-P;w-%MGC(V`*GjIRxx$6O<
zy?|H!DYLKU#01ZoLu+xKHqbH*Nh;%+A=B>oOB@AbzMQQA1>$gMPG6+h_gKl*2$EK*v%EOG9@3OVBI2VhtbOj$ukZtsCl=LfXQzPgO?aW__0Hl1Ym*vDTnk2*)Zq8XJErC*mUk
z$7mTCW}&?Ny-CQIm9H6?lpIJT^!d`bLwraMB5d{1dOX1&91UfZJ8O{OqT3UrKSudP7z{vEGrxh
zuZ*@f%o3P%HiQ$bWU_W-6qG(;b7!6~jG6zh*w%mdE-I|ox;;lHr9=MPnMi1yNJrfw
zEFy?b-GiBohqNyDdSgz>Y4?b^KQmjs-ac`?M%&2`Us)`8TbGO8wq7O+=E$ul&9*oD
z&AE9iWSfxeH97uA{iBJlmV4QluL46D0W+AqgxR@Tmy!EbmOHDp_xzzv;{HoD72RzO
z3+PC2F2uymB0|cTJ?9{4%l@GM$k{tUc{!ixsy8)mdi4)tG%#OG#jHF;0ek7djapas
zht63KC`=X^v|7wZKVJQJJQ|ot(g}kM!2)k~c=UG1Yv!DceYRXzS1^(wtl2_6~S{LQvkU^-T$sApxBw#`vo{w0jjmu*|JMNcNP__rwBX*U=7xC=G#f>Xz)q8r+XML!w787
z^5M3eF9y!J^Uq-Y)=1aV$wcFpU3p?Um_xxLA!_x4(UEhp@5lijMb>*IDfL_Xxh-3s
z2l2>klx3|VrV`1L!QbLK(;N8BxSyG0&cD=l`>-?z|Tr@ZYhfoD%N$K51OG<8@
zeXy6wvgRcxfQ1*B#`815PjVr|UU`TJcEMe~~U!x%QkWl
zX~6W^|B?%}y!X3axlq!l$c114+Z|rgVtq4r=A9clckROfNIjo!Z@N`VC!8pOh71n(
z31^?duTWVz4WNvO12A6l?cYo_)m|*6NKw|A>`_Z`VaPDxHl+!EHQAM{ui7~@>Gy7c
zX}^mX1p+hJsx$hH-{>5fvj#Y6_LofN>%B8K^7;P?$Pzz$q~wQv+n(bS;d}L{Nb6Oq
zb4iY`7YS{n)|pf9?ANE-N#%P%S(((e_j7wZ)Ke1dY#AUki7XTn`iLS|Ec3ihO{$v(
z0IFDsXhC(>OL+tO)i`qowJ33MiENXhrKvhU;%Vv2g}U&*LOCl!4i?pR6^)#+
zQk!3X-dggXB@O>qhs$$T6>M)WY2LSOEgX;ZkvM^X2u(-dF%#aUjLvV)R#o0Dgu55O
z5wBZ-5l&w+=w)MZ`8FM)jzpR--_&}say5~Pm{1hvFrDE*>GTDE5o8gq{_2CjNvZf~
zSw;{ZxA=1_hCgk#IS*(i-s9`t_>LY$!1(c3zD`}8a^A%ww8!MHt;^>Eo*`_gdooA{
z#}IrBPeUjm-GyJ7N{6n?nsfiIs~}6`hTkn@e4YnV-K|4V0RfQOoFL4OH3bu9yhn|g
z)_ZV~Yq#N-4_4jHM?;U%JzW(%5CLYh)e~0Ry1sWV>IGSp``SNQeM_Y6!ito?WhM|<
z0BK!aq`~@9#T`pORbIv$nP^wV*Z^=h;IwUU{4@4WVZDoH8r$G#Xz2Rk1~v|YGO7C2N=
zTf&erbJwuB_x%YJ3{$$A*Xd$kkqaH!y#7rj;h#mMgDON|WkP523nCz8PEtdHjM6YH
z6Ztt`Qa0QBW%KVm_RAC$<=LJ2>?OCDV*e^ORZoM{DZ7l|EKeC5%Lb2qz-n_;L8E6S
zl!dT$_dC`^d;9BLXVYX^xsOmsfTQ5J!RmUtsPFh@hW0qyYtPR1U#fhakkhDuKmaqC
zzRT`6a8uU^Q>wV`Bt8d=gT*4;;%Y)TFEJtOQ;&4imbmo&4
zW2gK%0x`#PCoJyumuUXz%h?h++%X*_L*s;G+{KG1kPNxqgu(gtu&MWc0R42B
zWG);mI=}nV$JnrEsYZwqEI?#!Zu#rhim{uVH{DZvBkzxrCIl{Xw(q{!xDJT~N24^Y
zMF=urA2@tHs?~m!aqBoG!9W5{O$ZW1x`pJRhPbLd1YkAV>yFYLD;R~ud#M3$Td
zBiwj*ZAVeZiJhOJ$v^?%lyo6OM^?qy4Qicb9owg0s$uun^VpF8St-<$OUg9B3D5xu
zxQZo_3%K6WW3lHp80@+2`kaCwU?}>R9YuxKs!#e#yT0D=HXI3zK{y%#1QpL3G~#)!
zD|Zkp(Oa^szL{GTc
z6c2|p!|j)gHPQCDaI|NzlulRxKt)b#iKv}NHy)Nadcd2cnkM;jDu4O
z$U>wLOJ1ENx8%{Bl9AQFOn_?G@VC2S-L1_v|B^KvV^HMjWVe@9}_@5ZgF`WSvi-j8QC3%{H!3bmMPEoy4~?#
zn?uRdUpX4S|7sy0_CARuBX(W{fG{~5R95Gvl4*1Q4hn`PyFPk-nXj>CDI4_-MIl~*
z^wzM>RrIWT;P|gK!zcDiJe4c<<9CJChqgb?gni2h1!O^SV9YIk&C!3@Cg)9e)b=u2
z@{DDd_H24tiU+4Eav=p|$^ADVOXb(w*FnihACY5VRsmUN-t`QnzmhKWQ?igUCwub)
zdjEOXNi-P{G%?!i>a4y{gaunCV#)9%DHh9-Asnz86AhcJALPxy?-k3B9$(B#PpCQg
z&F|%8csv0(nMQEIR5Ij`#;oEm-BT7QO@u4+qqY{mczoBLjK5_W=W8Ad$t>VVy`-{c
zyx;GpTUQf<*``!mjdwX5WaZyHR;42l7&v$)^Klr8&1-N<=0z~i~%lcQnCwu4_h
zoa*r`pu@p38O0=3R?czUYjlbsA&43;0iCVelId*FTFf1;;_NVR4pLI7
zZYbI9+Ie&pWJz>&z9qCZj6~^_2_==h>a4T+%-n}E24=KgO*&FMSs<2J9;-aKT#Weo
z$uKJLhOD4Hw`5VlPasR+(^oTqgsx~kcVwANr(DX|h-zb7!Ob(D;eZ-s&Cabr;d#Xc6YAB;+_mi
z5^>DvxM1l&{=>q|f$v`hYC3nlSCEJV?rN_+{T#)oR49USnA&ixXxyB?8uBtuUz_Ts
z{7w_BozQe_-*bF2I#QM;grhV`cgcX~b&l-qj;V|OB|rSxSoTJ^0{G&VcW+L0G%XNf
z;fYc@<&Ys+LNuy2RHHelvRHQY9X9TJ&~I|Dzu1lJoi}n%FPOgm#l@1hW4YjOAEvmQ
zq0YQ|%3^=B-<-R*0&7`1(sEGS^!eUb#8A(zaysHda51gR%soOHY_I1`SaIsw6eao6
zE^YPx?`E@+p8Ew~#|Q|)fNXTNLI%f&!)Gt}JIJkwfzD3j>5b3-L5c_F13GRK010d*
z12$^i`5!t9N1g&@ehIWv=3^hN`8yGdOvIVAK|nAX$tgKQnazJH88i3CUdf+g4r~^)
zPHzABbvPcJf--5f1QWEm@0bsP+Po%z!OEH&83WIrH6_Vz&wO8f&9fSoLm@?$rqdnD
zz3Ju`DHD4d8ocsfK3AP5woybx7}xy6&OZq0@K70&QBI|gxXT8=pflQbIHxc5)UW!(
zpmaR?iWuw~3M5vAXmnxR;XahN;F(9%HrvlY795e5w4OS0Kg_dklvHuD)^xF8^6mE-
zvrNrO>Q>{<56jX~|MKvK<12BFrT|Pxh{k++@T6P*ics=VXym;ADxs|I%eS0Bg6|EV
zI`k;QBwQ$lrBRdhLZ8VqeuKJfmx0Tf0H90+$9Jn3NgCR)`=jT@RBW&a!x*bIc9o2p
z@QB51Jswh-skRee&xyCzy{O?+8bQQh(r7!8QCP9TK4sBopthC)#F3TXw?JSRsYIb1*j959WC_4dWtF
zRB;^BnO-i>9dt-xT=s%2U;J%pPxa{)Tr4mIgJnt5T2qFc^2LsU6Au|HCPc4*Ec^>x
zW7P~P9d|&4CW<)~PnLlX|2H7Z>GxllDy4i+2pv_E6_7=+WqA-w=B5GDZ@&g)`QyTr
zziVa6-!)P~V1PNDJ*3sXDtr2z4ZahHV3^~c@K#mcBc+qsh@xmDRRKw*JIFvp8)~$+
znDa{-Eg9}MhS5aaBSvsP#i##leNcQ>>-HQN8jtutk`mqwK@tg6Z?CcS9ka1uNclhh
zn`FWpd7rM{^S=VJu+>LOj_v*QSvKOom*V0u3B!PsU>Q;Ay`;``9>tL(Tq53NvpU->
z1$iEg8IFLxJtCMrF5}|qA4f%k-eCfhHal$g3+BASL$=)fR#42-;AnvC3N$-9FIBq`
zN>=~?Jcv#v$8cd^AqguHO#qedD|9BqdGqk=)6cICjQ4}0I|
z{hrZcDrG=%GEmT`VlU-zesR90dLADQ%)z(>2~m2#A*bXC&43AqU8Kk~*HjJgHP`axG)O@Rz%z2cHk1rpmw)@iyMFQpfg9F5
za~~7wc_7`>IRq9Yfa;wd++bVXciQr`R}X(mxB2q>f11mM1CP>O?IR>00@PxwN6prC
z8IzW+vz3=8ueeaJzBSoey`1*8%z#7|$OgBEGT2@kaP#e-{I@R5Ox3>P%5Qc&$pw0r
zXi{O6M+l(F?em&-$kdOVW2e_C|1Z&f$<*}8yDxD;&pa^}G(spYne$8csLi%_Tnqoy
zi^Trx8ObsnJhZhS(bYINQ1$(vWG;n^xQ>VQ&Tj|acIUGvB1FUWOyK_I$+Btv8&D!J
zi=YE)h)V&0kw7sQMtXCt5ReA2Oy%TlC2jU~Wh3Svze=IM=DR=_KYPhdCxsXIrs}B>
zow5U*jB|SH+060*59Ae3XuA?kDnplcyk}zlftQ)Kni*0m=8_320v!3rb+(M(_9?r$
z$>(b+iMH0=8>l<+G=VTcq%a;en6{Tpyz>R^&^vy1Fz?-D^@y?N;FeWvPv=ybPFoP2
zCBftj{2EcKULLmav6}X8KXx#Yu4=$Nq-%f(6kS{D|4Hr)=`1=V4E9;MtSMo@0`c
z5hNr;U3Psn9gpWHY0knUDh5_tj}5=|)`!_ZcUk4B6OYlc$Rtb>ILu;gqih+EI){!s
z;pji5*Fg8zuFlRARPyVu$1tJbi;Sh1?0yb7}P7PDv1Vu?hLzUy*zR^0EkumWTNYMm9MutGWR86+lpN&pkJpfU@1
z5dn}cyNlF2-X1b-&WBgcvzV&G#b@_yeVhw>mgw0CPRjsj^U7bh4WIOpbM()j<$oGK
z#qgGkhuOeoO$=En1mCi{+
z7zbq*Um&&S-wz&j_xC^-L%Qo*i#t#3{1l4&dqEbIGIeAR9J>M1Xx7mKW`={`Y@5J@
z0}my8TBfSFI1J0QEFqF8;wX*lOkS9BeQ
zNbAkl_<1+1_w%-+L}z1*DcD(`oo)>pNgOu-(wM;`XbGQ=4I~nsg;*-2fffHSgpfFm
zJ)(7FJz6qr{#T$Qk!qrw9Ni5yZy`PfWJy?IR23#N%8u!6w%_J09N6;9&UyRS>qqs}
zS1+M^I`3A|Q3{qNnZs~Fp%@FODr7J@sxcTl4k#XFm<`O)6A@BPJ0u=`n(OXy<)1=NoG
zU5=uG>s)gm*rz<#?$2Id?5(R@&PIGAFd)jD%E0JyOCHG?aq}MSHOXAvpZ)DFe_h=Q
zCK#ANLa-#N%~8tke%dwZuIqvUwHc3y+C2IR37^3aXRc>
zMy8{Vj?&V2n5!D;F9d1R1-lWHIa=ciaAl1F;{jAVKV=vbk5yh-bj4r6(-Bs2|eeI
zti_nPf$3$kn9fv8y6rEiAUN!<$igc3eKwy9dmoa6-Q^^c0w_YrJYnQ%+!&YC+76vT
zeHNDKIxd~+f>6R&Hf(Gg1zbTe{=bL;KYJGIY^u!icXzxEcQlNI>7)rJ$QW?C&zLg{
z9{wfB0yuP8@XWU>ttWs6FNUSYB*Ad4iRE85Q=Sthed7r<~qRGTlA
z-gfUogUi-%?b!7CMQwgxg8JQ3-
zuHrCz)!4RO;mKbcbZ5T9j@vEBhp}tsWr}5JI-~V1p
zg%==f!p6%uu$BziXmIC#Xe}J~KOoDdfBc<@MJJ*RZQxNdp(+`=8`kMxFJJITO|SGr
zVYv=<*iL-B_Dwh%nSrL$Ite0aQ@=4E5_;SFSvSaJsoU`4a4sHwDAC$*H^s7u0v4h=
z~hT=BDaz
zJ{ia-BQJ}Au3?bCst}znggdeh<<5WTQMK)Vm@I+KZ#hLmTGDv@;Qg4uxe$zaL=-i}3lI^Kts0L5qw!IF^^p3z#I`)GRx
z+}YHKiz<%QihzJAqy4Mgic#+w2T%JBIGu{&zI0*FJA(yJ>yl*Mx%<_!3^?2%
zAbQ8{eiLqcS=E!iaCOhFUGvOnj!rz4Xs?}#LNE%cZ9c@9@#O%iKvutyTV_8Gwj90!
zvdGWFbw{l#JesnHn!EFaWK644X$?4~b_pz3o(gyd!%RE_6VencNg$07B9`CpBx$g`
z)Nj$V2bG3|GT%L)zPZ$Q@yrS??jM36f+VQTDPwNwlH!T;4nnyVKY=X53vA>0nTqi(
zL~5eAqwr+bfQpB0Hy7WqTd8u|jbO|40DI=9e%F93P4E3~Asz6n
zO#3@WDo7sb&g`LV&Q+OHZ`;6EpW=BLntA#7506X9cz;-6aR5YF!Z3lys1&X@HIsVF
zc~KCmpcLaFH71|IVC&Aw8Q{G(9Z^1VgsiFhW@tRrvr$x7d$L3jMn{dce8eUyv*JDX
zh@Z!h-{{(Lz5D)GKo*6`a{BNWPxGOkd&yJ~!`UPtF$};k0yqf(MuKEaXG)r!nfufh
z%kHQs{pe-+U{0osDVX+%vdI5WzW0$MXkFcRBx0g;n9Sz^COzHL+Hf16O69;DM`99y
z6c9&ACSoNCFfhuC8e?5?(?E0uJFrme5%Bz*qA@m-fDNa=(bqcru-0i!;L
zfQ)or`0h@DO%=eDE-YK}Pw1U_Pn4C8ZU4*Oz2bx8%2e@4C`U^8XM5Y~XR?WK86?Xp
z6jox}5f6pyKT!{y+?%-bbC6|~TS%r~pf6pR2C*p{tWpIyo8zb>x8%tJ
zQ(xt!?_J;&kVOc`Uy8S$pUI~pE}0-BvOWKV-jeh5z_Kw--7S{~Cwl%5Z|5BzRh_o~
z@2PXn^j^~|sgQ(T1VliJGzCFWKp@yw#Z_O+?y9?syQ{+5rHT|$K@g+~QWX$L0wf^;
z(%U4N-plE~-x&;xzV3?q$2-@Ri$7+Va>}=Sp8L7)=HRA%KAN@y%p_hoy-J-W=N(F3z>rs37G8l;me+`cAxoLF7&hy=s0&`
z100W}aWKvTrS@e1yC%LIZtIxX)Nt*gXvCk7V{l8^fc%ZlPWv5hzpo@r30eV>*NmDr
z`6ewXpnY6lUb4teNRhMgCU??h?kjtAasVRF51w|Vxjvsj+
z3VMeEnvx2bq^anh`A?|ih6~WZ@qzX)-!Gv2o@G6C-<1P_CRv#(2$ZIS!zMrUJXz?t
zxfo197CKfQx^iMEM1?i9L=mJ^*5f1RE_)n=370r&*VXO7)s3795<-K$?alLiPS@Z_
zm@>x&6hRP(k)afY@*xD`LlOv3sDOwGG5RiCZg_k6JrCZ5EWwIHxi#M&e2Vk-EYZ+j
zJj(GvnV$cKrgY@(D_N=!%S0p0)4M-gPeen71RaMVo(C}qB+TCg6cxlqTZV}W5}1Ne
zfx%&hLIfTLJgH(}OHL!9)jdC8!uVqVl|Zhm*K=EcIJlklb!P*XLnU%UM{?O6o8&6(
zI_db%-nMU2Qvx0BQ+r#kyd-4;8bXNUiH#o10MWY*h7_CtlRSr$G^~_&%Ph&e5UFCH
zQbS%%neu3$Xa9bJmtkY=2e-V21s!D)E(RC~0%EENjf>ta2w(*kF!6{Wm!LdOvoOr_
zkbuB|C2*cd$@_s+8UCJKJmc`MEue(PbIR|Jd_IeE^}eZacWW{6O_au7NSCyy>0c+L%{RB(*YE6WS1y(GcCYQweT5iO9Tddk~k$76f)qO|UWuF)8wC4ee
z(+B{C8Gthcj!-1)lK?t|0l*_Dsp2Ap)~7V3JykSy(E-rW5fMFE+B@r33q9>)fY>gC
z%N_EJvI8VZzM9z)vfsLZ6+g4<>QSBj^vM}b+Jy9gYTV1SttfEJ>AQO1~BH
zl70=MZ_BOYSMcgpUCY6D7t+427tydoAD|GRv}EoxXP0cS&V6RTcnsY;{5_dG5H$hoQ{zH&uT3UDARTHx(b^?+TtjkW+Vb-_x|mv4o6>kVOKNnYnN3N``GQkDYs^
z53)3Vy?G@Ya^Eem6c$AZHluLRzdTuLjz}4XEj+ht+gc>zFM@a$=A`n@v0qz&OjpVp<7=5&~*U>6K~?f6AHj;OBh`+W&NoBp}P>%CWswC)VOjSVIF85@foH
z{&!7zE$9Zv#R%u?pMNkY=ILAzvbQ{h2fZ?wVNe0aARfg5Ef$neF<*k?0E&lknZ7Zj
zXz=!!fE*9=P<#ID$8M{|FkyV#RF&fDZ2c!{ZyJHbLRyF*{G2tpLYJQPyI8^aR?+BL
z{4JuwBOHw{^;8`#M`OWa38`Si5|i7Kk+-OzFI`A%7h*wq=cQ9iVLD`ka0$!H^>rx|
z9+=O(JmR7y4%@O11&rmJ-VOocFsOw}tjns;R_dGZyY==T%P
zD{)MXUO!{5*|kxGEcbBHh=S#DD!q8v8oA!O$vE@j1RVJJ`E}Nvkb_9Hv})g%s}V8c
znu|j)L3El*hi#ClG+!i>Ibx*zf2COa5v1Sz$e)i7;`6m@|GJ2Aw3V}t_Mw310cy^w
z$JNHwh2!Ua(dKlrY9rI{;-{ayAw>Km1U4dN5a2EyGxa59Qua4w8{PeLb9MX9Rl|eb
z?MwX)R~Cy|LjkEWAgHvTjF`UgAJY6gdO>HMrgF{7m!YUK6uLIz6%?ANo|GpuGLKuy1Ohy=^WV@F&3>
zFF{2+7)YQwCc0X39QiE+~8=IaS)(r_lSgyx1{uv2FvQgDYHKY
zw!!{L#Yscs_uu@1^7c;RBJLy%#4%o=Ad2Sz3!yv*NjQc<7?MyjN}Z`jt~Kr_ZRP{K
zBD*>5M`$iWmYPdP28GJ90T{;uL=Z&TV5+l%_Lnirn@LA}N{(Yt(MS=MsCvvM+ernXXz|;73xYn^y#gDNL}3(2
zZ5apj`K9aAr!DJi7lN;>e|}Ni>v%NkXdejiF+k{&uSs;4_ljpfx}|RslTfm({lk5f
z-|=LmvvD{E!2r=1>M^xpeZlh+upp{1^pZNm%7Js{e+s4k
zwy3L{kj2%rL>2X+41xn?M(!J?!6P^8L{q>7WSL{C+q~+ZAn2TlMEy8}!knpJ|MhZ{
zZKM7ddbWE`e3cgLY@TVaI{G(+ibw>Bj6pS~BZH>Sc@Ymuum0M^+^1yue%q!uS%1$A
zEaH%nh$w8sfqzcz1PkzA}|WD1R4Nx<@TX-A6g0J-t&u)rFQq0x2WE($y6v{W-xhJUNrU)
zB9W~uS@63W(d5tBbT%c>RQGPo(KU?b7$q!MRQDe`=OGW?FwZ@7EfMs;>~jtqvQsW49TWDXksj(}pHCf)m_Xmom8
z56{7EN#USpW^?74SKxS5r=KhHM92~gdX_n^oGJ%A
zMRKGn0MzD#V`r5=53)LX`e0D=mus@QNU%I``I{vW6V=eTGDs<`$Hy#r@^RqgU7~!m
zar=k;Lmt-y{?2O;VN^&CalC|wd0DP~zf3lGwxuEC7%gI|w5j5I7bFJOm@K1Y&Tx5R>bBWlHUe(PQTv1d#}D
z_tfNcoIA8#hTe4bdiZBc0Zh0
zH=gnO{)~3D6d??U!U}x|*I5ttpFICrQ2ej+{`3FuX*{RAez9VhyQ}jhrA*qQH5h-;
zCS{zrB^NhBR%^n)>+;@D$9h|99*lOk+(QacIY#jq0;4o8Rdp-$DODP+`Xu9S9qZ|7
z8y1O#lOR&Tu?mw%Zcbk@V4|pG5g|*hxwE5n71!N57DOWkgitw@IYSRfl;o?Kb06#a
zg+)Kyuz!^n^+ft}p}@nQ)`pQxENp~1aG^Bc)`3Feg=zDgB;FD(Tno^CTA$vNY{~eIvS5t!hpuIshS;{itjU?K_v5nzEA1FvC0
zGzAqnSl~r}8%!RC)uu{ZYxrc~xcm3@6%uZ1Q2OyjV#`qL<#UsJny$XDpu!TKqhM5_
ztTPssd~MchuGAmd_acbXc>o~+CDpcN4;lM3E>)jZ-95*9>*vJJAIPaWy7y_u-MfU0
z1aJnG07X{bo7(>bSuldlC?52JKK)1I+djzh)z-CeSd4I{VOAnSmT51Ldg&4Czz5u&
zd$(pqe6G1Y*K3yWk)R%fSP6n8Fkb*yCsnI8+V7-%NFQ}~-x+W?3J@Lxf>IxYwZ@IP
z)91e~z0%@w!L8*TRp*wm0bel=W1OJS26Y*E3sOhT{a=v9ac+BY*M*~tgRS*X!|@p4
zNfRs6r>q?`{@(YRTtb`Gq%FMo#fNW0QO^hr#2^;tMM)Td0L*c`0B9b9SQHfy1mz6Y+xwKZ{~59z)j2NLj_tj0YOREcYB&UkV1=%t@Xjf(
zd4Gf~BGixsK@v+x?+kRbJ{k*pvt!|qj2Bo)CdB|B^#BYO1PH+d8X_55q70+D^dDq8
z%evC36OP|jyNw{Ajk`Ze>1l3%8}Dfx0mp+{L@MEbGmb6Y={*sMxY-
z-PvWd*ImqTFw1Gu+({V)iwXwMI4eRe(US$@L3!83Q%eylWJ55F6=b?P+uaZTB4laW
zyLzF!_VUuGqh}176Tryh@X0#gNIf_uZC~rS63M%IR~%-JR_lWua~(&Bkz`(bZ_n
zMsCW>8T%plQOR;;!=Fd6k?@Ow#@Y!O8^Z{ts#j{T?6yu@`ikOb5IX@`Ixm?oZ+rg@
zKIplJ566@Yi&3cqhJ2*dTQ(W*U3%r0AWP@j9a=y^lP(_J|0)vmWy&RpU!gahF%}JZ
zCJ9UHytOL*Tkh)wgeWONfh>RkixmJs@c^WW#oJpI(NOJ%KQCf>+LqD1?ZYq_1&BGT
z4%eDi<&VB^rwCaLM$oU~vkh+w(clPJU`PnZ-TA}sd`Ygg9wvH#{pXOSZRb0~!}gYC
z!Ggri$`ui6#Cot
zWVyWYPh**A@Hubm)rmMq!%~&bPU@{+q|ThXQknF>AqyzY)m3i)`zk)rZSep(EbE3BB!u0Tq)WQl$qq*p8(x5|u2sKo&777~&u9t~~RI
zgkzEroS-Q}QI|P+&O@3MbD~{Hge-Ai@3K(MxrCAhB9tLmX*e`^)TLQe8jUGvh=fVKWncct@$boU2c7O~Rc_w3X;7@YacQXjTsg|c0V`E9u+Fl#
zWa524fh?74Ul}PxL%)x-Uz#fL42r4Ey`(;6SLvh$uZitKT}zfox{rM}Bku2B7tZ!?%oX9+b~>1=D7$piw!87i)f2skIp9efs3>t8Qj@Z3!$i(}gd
zOPuwUiFP3b7Txfa5p{arPauov$wD_?zR@o9BV;kL&0!
z2WXScE-G1ar_Xpxl{`E#cV7SS_X{Jg?gV5(5EP&~tAo&6R~OB^|6|BH&@JA>O?0n?
z4tl4?g6{i6Ui&aQUk
zveefe9~AJoKIVNr$sEs1FpZ(sSUhZVrmf&ZF)ix9DbD=bSoD7aS>hGPbI%?9{CURP
z`;aW|LuGMa0=jA3!r04f(`>I5CFpQ;Kq=NCR#jr1OagvfGCDBak;WNXXIUvNVUpKx}gii
zWP{s&B&tR6uE`Bzd{odiQpU#wL||A#t_Xw~RvKhDDT|Z#!ja?uf#L8@
z=x?c82I2vd#h^GYmz`H>b^Bv6sv*f{wHtNGWLsVBEWfk+aVQ!jXp{h?HSJSV$%wVq
zyC3{s{A_WOtKazIqNu0)(NJ&mKmp#c&3^nRkmYIuvbdg%bTtjfAOVP8
z9f-=XzW7$ivU~INX#M$e#@;ZQgg6k|5FtxA8e18wtC$GI
zLPkU;a{+7GDRXxI)BhW?1YY+yU7W(keKtrc^>OBmAJmqV=Sm7vn!1|ce!-4w4|=Xv
zK1)Dxz>{bk*JyT^OuBa^R63*n*5jRM7fK~{6+1tAm-F|I=VM_FgUW+gzu|i^spKDn
zA9x{gTd2~FSK}zxNq$KnAL9f5e9gN%Ju!Kca+LI%uOj{A_=`Oic
zasDaF?;lA5!Izb4`99$H^z%idwuk^r0I9iFH2Ut}Lt6a2_BP@o`@XNY@>^B3vw2`s
z<=GFhkSkpdbF@k+Ya_JkOO5uPdng8|WJ>M1to)))ASrEsaqjMM5+)*7lW2$5
zWIWl{**%@6Su-M4c1o>jd!_lgf6Tb6T&!O6jbE=Th`W2seKjYSVLU^M5tM?$cyRdK
zQ!
zCR3=JFw$_6g7imK8dHxcMQsl{S|<6O%@2BAtwY2lBTSKAPbkdq44U-t_P&i?@a*A!
zSB@WeiuQCbmc>018bN_FBmYh7;BlL88?u}|OGXe+&gBC;)(fFP2|~va1VP;T