diff --git a/app.vue b/app.vue
index b9b12906..95d948ca 100644
--- a/app.vue
+++ b/app.vue
@@ -1,23 +1,30 @@
.flex.gap-10.ml-4.mr-2
- VerticalNav(v-if="cvCookie")
+ VerticalNav(v-if="cvCookie && width > 900")
.flex.flex-col.gap-5.min-h-screen.grow
- CVHeader
+ div(class="flex flex-col justify-center items-center")
+ form.well.well-sm
+ VerticalNavHamburger(v-if="cvCookie && width <= 900" :hamburgerOpen="hamburgerOpen")
+ CVHeader(:hamburgerOpen="hamburgerOpen" @hamburger="hamburgerOpen = !hamburgerOpen")
NuxtPage
CVFooter
@@ -66,11 +63,4 @@ div
diff --git a/components/CVHeader.vue b/components/CVHeader.vue
index 347d08d6..0dc4d897 100644
--- a/components/CVHeader.vue
+++ b/components/CVHeader.vue
@@ -1,8 +1,11 @@
@@ -48,7 +55,12 @@ ClientOnly
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
-
+ button.items-center.px-2.py-2.text-sm.rounded-md.cursor-pointer(
+ class='hover:text-black bg-white'
+ @click="handleHamburgerClick"
+ v-if="width <= 900"
+ )
+ p.uppercase.white.w-max.font-bold |||
div.max-w-min.mx-auto.flex.gap-2(v-else)
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'
@@ -68,12 +80,14 @@ ClientOnly
| RESOURCES
NavLinkButton(to='https://carsonsvillage.org/get-involved/' target="_blank")
p.pl-2.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
-
- .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"
+ button(@click="handleHamburgerClick" v-if="width <= 900")
+ p.pl-2.uppercase.white.w-max.text-black-999 |||
+ .flex.w-max.px-2.pt-2.gap-2(v-if="isNotSearch")
+ input(id="search" 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`")
img(src="/CVSearchIcon.png" style="height: 40px")
diff --git a/components/NavLinkButtonHamburger.vue b/components/NavLinkButtonHamburger.vue
new file mode 100644
index 00000000..709e81bb
--- /dev/null
+++ b/components/NavLinkButtonHamburger.vue
@@ -0,0 +1,28 @@
+
+NuxtLink.items-center.text-7xl.font-medium.rounded-md.cursor-pointer.no-border.relative(style="text-align: center; overflow: visible;" class='text-gray-999 hover:text-black transition duration-300' :to="to" active-class="active")
+ span.button-content
+ slot
+
+
+
+
+
+
\ No newline at end of file
diff --git a/components/VerticalNavHamburger.vue b/components/VerticalNavHamburger.vue
new file mode 100644
index 00000000..af1e4769
--- /dev/null
+++ b/components/VerticalNavHamburger.vue
@@ -0,0 +1,59 @@
+
+
+
+ .text-center.p-2.pt-32.pr-12(v-if="hamburgerOpen")
+ div.gap-2(v-if="isLoggedIn && toggle")
+ NavLinkButtonHamburger(: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.mb-2.w-max Pages
+ .bar.mx-20(style="border-top: 0.5px solid #646464;")
+ NavLinkButtonHamburger(to='/EditPage/0' :class="{'!text-black border-green-999 bg-white': route.path.includes('/EditPage')}")
+ p.uppercase.white.mb-2.w-max New page
+ .bar.mx-20(style="border-top: 0.5px solid #646464;")
+ NavLinkButtonHamburger( v-if="isAdvocateAdmin" to='/Users' :class="{'!text-black border-green-999 bg-white': route.path.includes('/Users')}")
+ p.uppercase.white.mb-2.w-max Users
+ .bar.mx-20(style="border-top: 0.5px solid #646464;")
+ NavLinkButtonHamburger( v-if="isAdvocateAdmin" to='/EmailList' :class="{'!text-black border-green-999 bg-white': route.path.includes('/EmailList')}")
+ p.uppercase.white.mb-2.w-max Email List
+ .bar.mx-20(style="border-top: 0.5px solid #646464;")
+ NavLinkButtonHamburger( v-if="isAdvocateAdmin" to='/EditUser/0' :class="{'!text-black border-green-999 bg-white': route.path.includes('/EditUser')}")
+ p.uppercase.white.mb-2.w-max Invite user
+ .bar.mx-20(style="border-top: 0.5px solid #646464;")
+ NavLinkButtonHamburger( v-if="isAdvocateAdmin" to='/EditFamily' :class="{'!text-black border-green-999 bg-white': route.path.includes('/EditFamily')}")
+ p.uppercase.white.mb-2.w-max Edit Family
+ .bar.mx-20(style="border-top: 0.5px solid #646464;")
+ NavLinkButtonHamburger(to="/" :class="{'!text-black border-green-999 bg-white': route.path == '/'}")
+ p.uppercase.white.mb-2.w-max Profile
+ .bar.mx-20(style="border-top: 0.5px solid #646464;")
+ NavLinkButtonHamburger(v-if="isAdmin" to='/FamilyTransactionList' :class="{'!text-black border-green-999 bg-white': route.path == '/FamilyTransactionList'}")
+ p.uppercase.white.mb-2.w-max Donations
+ .bar.mx-20(style="border-top: 0.5px solid #646464;")
+ NavLinkButtonHamburger(v-if="isAdvocateAdmin" to='/FamilyReports' :class="{'!text-black border-green-999 bg-white': route.path == '/FamilyReports'}")
+ p.uppercase.white.mb-2.w-max Family Reports
+
+
diff --git a/components/imagePreview.vue b/components/imagePreview.vue
index e4c5a154..5e88c213 100644
--- a/components/imagePreview.vue
+++ b/components/imagePreview.vue
@@ -75,7 +75,7 @@ const removeImage = async (image: Image, isPreview: boolean) => {
emit("profileImage", emptyImage)
previewCuid.value = ""
}
- if(image?.url === props.profileImage.url && imagesTemp.length !=0){
+ if(image?.url === props.profileImage.url && imagesTemp.length !=0){
emit("profileImage", imagesTemp[0])
}
}
diff --git a/pages/EditFamily/[id].vue b/pages/EditFamily/[id].vue
index e6a951fd..753fc518 100644
--- a/pages/EditFamily/[id].vue
+++ b/pages/EditFamily/[id].vue
@@ -20,54 +20,73 @@ import {
import { vElementSize } from '@vueuse/components'
const cvuser = useCookie('cvuser')
+
+const data_family = ref({
+ cuid: "",
+ stripe_account_id: "",
+ created_at: new Date(),
+ updated_at: null,
+ family_name: "",
+ advocateCuid: cvuser.value.cuid
+})
+
+
const bottomHeight = ref(0)
+
const data_user = ref({
cuid: "",
+ isActive: true,
first_name: "",
last_name: "",
email: "",
middle_name: "",
user_role: "family",
phone: "",
- address: "",
Pages: [],
familyCuid: "",
- AdvocateFamily: []
+ AdvocateFamily: [],
+ Family: {},
//PageDonations: [],
//DonationPayouts: []
})
+//const data_all_users = ref([])
+const errorInPage = ref(false)
+const errorToUser = ref("")
+const useExistingUser = ref(false)
const userCuid = ref("")
+
const router = useRoute()
const cuid = computed(() => router.params.id as string);
-const currentUser = computed(() => data_all_users.value.all_family_users.find((({ cuid }: User) => cuid === userCuid.value )) || {})
const isAuthorized = computed(() => { cvuser.value?.user_role as string == "advocate" || cvuser.value?.user_role == "admin"})
-const errorInPage = ref(false);
+const currentUser = computed(() => data_all_users.value.all_family_users.find((({ cuid }: User) => cuid === userCuid.value )) || {})
const { data: data_all_users } = await useFetch('/api/users', {
method: 'GET',
- query: { page_number: 0, sortedColumn: "first_name", order: "asc", familyCuid: cuid.value },
+ query: { page_number: 0, sortedColumn: "first_name", order: "asc", familyCuid: "0" },
default() {
return [] as any
}
-})
+ })
-const { data: data_family } = await useFetch(`/api/family/${cuid.value}`, {
- method: 'GET',
- default() {
- return {} as any
- }
-})
+const getFamily = async () => {
+ const { data: data_family_db } = await useFetch(`/api/family/${cuid.value}`, {
+ method: 'GET',
+ })
+ data_family.value = data_family_db.value as unknown as Family
+}
+if(cuid.value !== '0'){
+ await getFamily()
+}
// Method that creates a new family on the backend and adds the first user
-const createFamily = async () => {
+const saveFamily = async () => {
+ const firstUser = useExistingUser.value ? currentUser.value : data_user.value
if(isAuthorized){
const result = await $fetch('/api/family', {
- method: (cuid.value !== '0' ? 'PUT' : 'POST'),
- body: ({family_name: data_family.value.family_name, ...data_user.value})
- })
-
- if(result){
+ method: (cuid.value as string) !== "0" ? 'PUT' : 'POST',
+ body: ({family_name: data_family.value.family_name, ...firstUser, existingUser: useExistingUser.value, familyCuid: cuid.value})
+ if(result){
errorInPage.value = false;
await navigateTo('/Users')
} else {
@@ -76,54 +95,80 @@ const createFamily = async () => {
}
}
+})
+const userCuid = ref("")
+
+
+
//todo add the ui back for this
-/*const onResize = ({ height }: { height: number }) => {
+const onResize = ({ height }: { height: number }) => {
console.log(height)
if(height != 0) {
bottomHeight.value = height + 150
} else {
bottomHeight.value = 20
}
-}*/
+}
CVContainer
- form.well.well-sm
- TitleComp Family Creation
+ .well.well-sm
+ TitleComp {{ cuid == '0' ? 'Create' : 'Edit' }} Family
br
- .information.rounded-md.mx-9.my-2.text-center(class="sm:text-start text-white bg-blue-999")
- CVLegend Family Information
- .py-4.grid(class="sm:grid-cols-3")
- CVLabel(for="family_name") Family Name
- .col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
- CVInput(id="family_name" v-model='data_family.family_name' placeholder="(user defined)" required="required")
- .information.rounded-md.mx-9.my-2.text-center(class="sm:text-start text-white bg-blue-999")
- CVLegend First User Information
- .py-4.grid(class="sm:grid-cols-3")
- CVLabel(for="email") Email
- .col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
- CVInput(id="email" v-model='data_user.email' type="email" placeholder="(user defined)" required="required")
- .py-4.grid(class="sm:grid-cols-3")
- CVLabel(for="first_name") First Name
- .col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
- CVInput(id="first_name" v-model='data_user.first_name' placeholder="(user-defined)" required="required")
- .py-4.grid(class="sm:grid-cols-3")
- CVLabel(for="middle_name") Middle Name
+ .bar.mx-9(style="border-top: 0.5px solid #646464;")
+ br
+ .py-4.grid(class="sm:grid-cols-3" v-if="cuid == '0'")
+ CVLabel Use Existing User
.col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
- CVInput(id="middle_name" v-model='data_user.middle_name' placeholder="(user defined, optional)")
- .py-4.grid(class="sm:grid-cols-3")
- CVLabel(for="last_name") Last Name
+ input(style="line-height: 0px" type="checkbox" v-model="useExistingUser")
+ .py-4.grid(class="sm:grid-cols-3" v-if="useExistingUser || cuid != '0'")
+ CVLabel Family Name
.col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
- CVInput(id="last_name" v-model='data_user.last_name' placeholder="(user-defined)" required="required")
- .py-4.grid(class="sm:grid-cols-3")
- CVLabel(for="phone") Phone
+ CVInput(v-model='data_family.family_name' placeholder="(user defined)" required)
+ .py-4.grid(class="sm:grid-cols-3" v-if="useExistingUser" v-element-size="onResize")
+ CVLabel Current User
.col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
- CVInput(id="phone" v-model='data_user.phone' placeholder="(user defined, optional)")
- .col-md-10.py-2
- ActionButton(@click="createFamily()" class="transition duration-300 bg-orange-999 hover:bg-green-600") Save
- .py-4.grid(class="sm:grid-cols-3" Style="color:red" v-if="errorInPage")
- CVLabel(for="error_label") Error Creating Family and First Family Member in the System.
+ Listbox.shadow-sm.border.border-1.rounded-lg(as='div' v-model="userCuid")
+ .relative
+ Transition(
+ leave-active-class='transition ease-in duration-100'
+ leave-from-class='opacity-100'
+ leave-to-class='opacity-0'
+ )
+ ListboxOptions(as='div' class='w-full absolute z-10 mt-10 bg-white shadow-lg max-h-60 rounded-md px-2 py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm')
+ ListboxOption(as='div' v-for="user in data_all_users.all_family_users" :key="user.cuid" :value="user.cuid" class="px-2 border border-grey-500 py-1 my-1") {{ user.first_name + " " + user.last_name }}
+ ListboxButton(class='text-left bg-white relative rounded-md pl-2 pr-10 py-2 sm:text-sm w-96') {{ userCuid ? currentUser.first_name + " " + currentUser.last_name : 'Select user to add to family' }}
+ .div(v-if="!useExistingUser && cuid === '0'")
+ .py-4.grid(class="sm:grid-cols-3")
+ CVLabel Family Name
+ .col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
+ CVInput(v-model='data_family.family_name' placeholder="(user defined)" required)
+ .py-4.grid(class="sm:grid-cols-3")
+ CVLabel Email
+ .col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
+ CVInput(v-model='data_user.email' type="email" placeholder="(user defined)" required)
+ .py-4.grid(class="sm:grid-cols-3")
+ CVLabel First Name
+ .col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
+ CVInput(v-model='data_user.first_name' placeholder="(user-defined)" required)
+ .py-4.grid(class="sm:grid-cols-3")
+ CVLabel Middle Name
+ .col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
+ CVInput(v-model='data_user.middle_name' placeholder="(user defined, optional)")
+ .py-4.grid(class="sm:grid-cols-3")
+ CVLabel Last Name
+ .col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
+ CVInput(v-model='data_user.last_name' placeholder="(user-defined)" required)
+ .py-4.grid(class="sm:grid-cols-3")
+ CVLabel Phone
+ .col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
+ CVInput(v-model='data_user.phone' placeholder="(user defined, optional)")
+ .col-md-10.py-2
+ ActionButton(@click="saveFamily()" class="transition duration-300 bg-orange-999 hover:bg-green-600") Save
+ .py-4.grid(class="sm:grid-cols-3" Style="color:red" v-if="errorInPage")
+ CVLabel Error Creating Family and First Family Member in the System.
+ .div(:style="{ 'margin-bottom': bottomHeight + 'px'}")
diff --git a/pages/EditPage/[EditPageId].vue b/pages/EditPage/[EditPageId].vue
index 2edc09f5..8f634100 100644
--- a/pages/EditPage/[EditPageId].vue
+++ b/pages/EditPage/[EditPageId].vue
@@ -21,12 +21,13 @@ import {
import { ChevronDownIcon, ChevronLeftIcon } from '@heroicons/vue/24/solid'
import { vElementSize } from '@vueuse/components'
+
+
import type { Image, Page, User, PageDonation, Reply } from '@/types.d.ts'
import type { Family } from "@prisma/client"
const router = useRoute()
const cvuser = useCookie('cvuser');
-const cvuser2 = useCookie('cvuser')
const data = ref({
cuid: "",
@@ -35,11 +36,14 @@ const data = ref({
page_last_name: "",
day_of_birth: null,
day_of_passing: null,
+ age: 0,
visitation_date: null,
visitation_location: "",
visitation_address: "",
visitation_description: "",
+ visitation_end_time: null,
funeral_date: null,
+ funeral_end_time: null,
funeral_description: "",
funeral_location: "",
funeral_address: "",
@@ -77,52 +81,18 @@ const data = ref({
email: '',
middle_name: '',
phone: '',
- address: '',
Pages: [],
familyCuid: '',
- AdvocateFamily: []
+ Family: {},
+ AdvocateFamily: [],
},
FamilyDonations: [],
advocateCuid: ""
}
})
-type User2 = {
- cuid: string
- first_name: string,
- last_name: string,
- user_role: string,
- email: string,
- middle_name: string,
- phone: string,
- Pages: Page[],
- Family: {
- cuid: string,
- family_name: string,
- stripe_account_id: string,
- created_at: string,
- updated_at: string,
- FamilyMembers: [],
- FamilyDonationPayouts: [],
- Pages: [],
- AdvocateResponsible: User,
- FamilyDonations: [],
- advocateCuid: ""
- }
- //PageDonations: PageDonation[]
- //DonationPayouts: DonationPayout[]
-}
-const data_family = ref({
- cuid: "",
- stripe_account_id: "",
- created_at: null,
- updated_at: new Date(),
- family_name: "",
- advocateCuid: cvuser2.value.cuid
-})
const isAdvocate = computed(() => cvuser.value?.user_role == "advocate" || cvuser.value?.user_role == "admin")
-const data_all_users = ref([])
const replies = ref([])
@@ -130,17 +100,24 @@ const imageData = ref([])
const profile_image = ref("")
const toggleFiddlyBit = ref(true)
const familyCuid = ref("")
+const disableSubmit = ref(false)
+const errorToUser = ref("")
const cuid_data = computed(() => router.params.EditPageId);
const cuid = cuid_data.value as string
const user_cuid_data = computed(() => cvuser.value?.cuid)
const user_cuid = user_cuid_data.value as string
+const { data: Families } = await useFetch('/api/family', {
+ method: 'GET'
+})
const pageCuid = computed(() => router.params.EditPageId)
const disableCriteria = computed(() => !data.value?.page_first_name || !data.value?.page_last_name || (!familyCuid.value && isAdvocate.value) )
data.value.cuid = cuid;
data.value.userCuid = user_cuid;
-console.log(data.value)
+//console.log(data.value)
const errorInPage = ref(false)
+
+
// Method that saves form data to the database for a page that has cuid: router.params.EditPageId
const save = async () => {
if(isAdvocate.value) {
@@ -153,7 +130,6 @@ const save = async () => {
data.value.start_date = new Date()
}
- // todo: change to $fetch
const saveSuccess = await $fetch('/api/page', {
// Checks if there is a pre-existing page to edit or if to create a new page
method: router.params.EditPageId !== "0" ? 'PUT' : 'POST',
@@ -173,43 +149,49 @@ const save = async () => {
console.log(e)
}
};
-const currentFamily = computed(() => data_all_users.value?.find(({ cuid }: Family) => cuid == familyCuid.value) || {});
+
+const currentFamily = computed(() => Families.value?.find(({ cuid }: Partial) => cuid == familyCuid.value) || {});
// Method to populate the form when editing a pre-existing page
const getData = async (cuid: string) => {
- console.log(data_all_users)
- const { data: pageData } = await useFetch('/api/page', {
- method: 'GET',
- query: { cuid: cuid }
- })
- if (pageData.value) {
- data.value = pageData.value as unknown as Page;
- familyCuid.value = data.value.familyCuid
- imageData.value = data.value?.Images as unknown as Image[]
- // Not nessesary with proper logic in watchers?
- // 1st case is handling getting the profile image from the images in imageData
- // 2nd case is handling corrupt values of profile image of if the profileImageCuid exists
- // but is not in imageDaconst currentFamily = computed(() => data_all_users.value?.find(({ cuid }: Family) => cuid == familyCuid.value) || {});ta
- if (imageData.value?.length != 0 && (data.value.profileImageCuid == "" || imageData.value?.find((i: Image) => i.cuid == data.value.profileImageCuid) == undefined))
- data.value.profileImageCuid = imageData.value[0].cuid;
- // handling the case where all the images where deleted but the profile image was not cleared out
- } else if (imageData.value?.length == 0 && data.value.profileImageCuid !== "") {
- data.value.profileImageCuid = ""
- }
-
- /*
- * Checking if donation_goal and conversly amount raised is of type number in order to prevent
- * donation_goal and amount_raised from becoming NaN due to applying the donationFormat function to a string.
- */
- if (typeof data.value.donation_goal === 'number') {
- data.value.amount_raised = donationFormat(data.value.amount_raised as unknown as number).replace("$", "");
- data.value.donation_goal = donationFormat(data.value.donation_goal as unknown as number).replace("$", "");
- }
- replies.value = data.value?.Reply
+ //const pageFoundFromUser = cvuser.value.Pages.find((i: Page) => i.cuid == router.params.EditPageId) != undefined
+ //const pageFoundFromFamily = cvuser2.value.Family?.Pages.find((i: Page) => i.cuid == router.params.EditPageId) != undefined
+ // Allowing access to the user's EditPage only if user is an advocate or it is one of the family's family pages.
+ const { data: pageData } = await useFetch('/api/page', {
+ method: 'GET',
+ })
+ if (pageData.value) {
+ data.value = pageData.value as unknown as Page;
+ familyCuid.value = data.value.familyCuid
+ imageData.value = data.value?.Images as unknown as Image[]
+ // Not nessesary with proper logic in watchers?
+ // 1st case is handling getting the profile image from the images in imageData
+ // 2nd case is handling corrupt values of profile image of if the profileImageCuid exists
+ // but is not in imageData
+ // const currentFamily = computed(() => Families.value?.find(({ cuid }: Family) => cuid == familyCuid.value) || {});
+ if (imageData.value?.length != 0 && (data.value.profileImageCuid == "" || imageData.value?.find((i: Image) => i.cuid == data.value.profileImageCuid) == undefined))
+ data.value.profileImageCuid = imageData.value[0].cuid;
+ // handling the case where all the images where deleted but the profile image was not cleared out
+ } else if (imageData.value?.length == 0 && data.value.profileImageCuid !== "") {
+ data.value.profileImageCuid = ""
}
+ /*
+ * Checking if donation_goal and conversly amount raised is of type number in order to prevent
+ * donation_goal and amount_raised from becoming NaN due to applying the donationFormat function to a string.
+ */
+ if (typeof data.value.donation_goal === 'number') {
+ data.value.amount_raised = donationFormat(data.value.amount_raised as unknown as number).replace("$", "");
+ data.value.donation_goal = donationFormat(data.value.donation_goal as unknown as number).replace("$", "");
+ }
+ replies.value = data.value?.Reply
+}
-
+watch([data], () => {
+ if(data.value.day_of_birth && data.value.day_of_passing) {
+ data.value.age = Math.max(Math.round(((new Date(data.value?.day_of_passing)).getTime() - (new Date(data.value?.day_of_birth)).getTime()) / (1000 * 3600 * 24 * 365.25)), 0)
+ }
+}, {deep: true})
// Method that saves images to the frontend on image upload.
const saveImage = async (theImage: Image) => {
@@ -221,7 +203,6 @@ const saveImage = async (theImage: Image) => {
}
};
-
// Method to set an uploaded image as the profile image of a page
// There is no network request because the profiile image cuid is saved with the rest of the form
const setProfileImage = async (theImage: Image) => {
@@ -237,23 +218,15 @@ watch(imageData, async () => {
if (imageData.value.length == 0) {
data.value.profileImageCuid = "";
}
-
}, { deep: true })
watch(data, async () => {
- const profileImageNotFound = data.value.Images.find((i: Image) => i.cuid == data.value.profileImageCuid) == undefined
+ const profileImageNotFound = (data.value.Images.find((i: Image) => i.cuid == data.value.profileImageCuid)) == undefined
if (data.value.Images.length != 0 && (data.value.profileImageCuid == "" || profileImageNotFound)) {
data.value.profileImageCuid = data.value.Images[0].cuid
}
}, { deep: true })
-if( isAdvocate.value ) {
- const { data: Families } = await useFetch('/api/family', {
- method: 'GET'
- })
- data_all_users.value = Families.value as unknown as Family[]
-}
-
const updateSuspendButton = (reply:Reply, suspend:boolean) => { // updates the front end of the suspend button
replies.value.filter((rep:Reply)=> { // ignore filter error for now
if (rep.cuid == reply.cuid) {
@@ -261,11 +234,11 @@ const updateSuspendButton = (reply:Reply, suspend:boolean) => { // updates the f
}
})
-
return;
}
await getData(useRoute().params.EditPageId as string)
+
const profileImage = computed(() => data.value?.Images.find((i: Image) => i.cuid == data.value?.profileImageCuid))
const profileImgHeight = ref(40)
// moves the listbox options (dropdown) down based on the current button image's rendered height
@@ -276,6 +249,8 @@ const onResize = ({ width, height }: { width: number, height: number }) => {
profileImgHeight.value = 40
}
}
+
+
@@ -311,7 +286,7 @@ CVContainer
leave-to-class='opacity-0'
)
ListboxOptions(as='div' class='w-full absolute z-10 mt-10 bg-white shadow-lg max-h-60 rounded-md px-2 py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm' )
- ListboxOption(as='div' v-for="family in data_all_users" :key="family.cuid" :value="family.cuid" class="px-2 border border-grey-500 py-1 my-1") {{ family.family_name }}
+ ListboxOption(as='div' v-for="family in Families" :key="family.cuid" :value="family.cuid" class="px-2 border border-grey-500 py-1 my-1") {{ family.family_name }}
ListboxButton(class='text-left bg-white relative rounded-md pl-2 pr-10 py-2 sm:text-sm w-96') {{ familyCuid ? currentFamily.family_name : 'Select family to add the page to' }}
ImagePreview(v-model:images="imageData" :images="data.Images" :profileImage="profileImage" :pageCuid="cuid_data" @profileImage="setProfileImage" @images="setImagesPreview")
.information.rounded-md.mx-9.my-2.text-center(class="sm:text-start text-white bg-blue-999")
@@ -342,7 +317,6 @@ description="Here, you select from photos you uploaded to show up at the top of
CVChevronLeft.h-4.text-grey-500.z-3(class="inline-block size-4 h-2 max-w-8" v-if="toggleFiddlyBit")
CVChevronDown.h-4.text-gray-500.z-3(v-else class="inline-block size-4 h-2 max-w-8")
-
.py-4.grid(class="sm:grid-cols-3")
CVLabel(for="day_of_birth") Day of Birth
.col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
@@ -350,13 +324,24 @@ description="Here, you select from photos you uploaded to show up at the top of
.py-4.grid(class="sm:grid-cols-3")
CVLabel(for="day_of_passing") Day of Passing
.col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
- CVDatepicker(id="day_of_birth" v-model='data.day_of_passing')
+ CVDatepicker(v-model='data.day_of_passing')
+ .py-4.grid(class="sm:grid-cols-3")
+ .flex
+ CVLabel(for="age") Age
+ CVHelpButton(class="inline-block"
+ description="If the date of birth of the person is not known, add an approximate age the person lived to.")
+ .col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
+ CVInputNumerical(id="age" type='number' v-model="data.age")
.information.rounded-md.mx-9.my-2.text-center(class="sm:text-start text-white bg-blue-999")
CVLegend Visitation Information
.py-4.grid(class="sm:grid-cols-3")
CVLabel(for="visitation_date") Date
.col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
- CVDatepicker(id="visitation_date" v-model='data.visitation_date')
+ CVDatepicker(v-model='data.visitation_date')
+ .py-4.grid(class="sm:grid-cols-3")
+ CVLabel(for="visitation_end_time") End Time
+ .col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
+ CVDatepicker(id="visitation_end_time" v-model='data.visitation_end_time')
.py-4.grid(class="sm:grid-cols-3")
CVLabel(for="visitation_location") Location
.col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
@@ -376,6 +361,10 @@ description="Here, you select from photos you uploaded to show up at the top of
CVLabel(for="funeral_date") Date
.col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
CVDatepicker(id="funeral_date" v-model='data.funeral_date')
+ .py-4.grid(class="sm:grid-cols-3")
+ CVLabel(for="funeral_end_time") End Time
+ .col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
+ CVDatepicker(v-model='data.funeral_end_time')
.py-4.grid(class="sm:grid-cols-3")
CVLabel(for="funeral_location") Location
.col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
@@ -418,7 +407,7 @@ description="Here, you select from photos you uploaded to show up at the top of
CVSuspendButton(:suspend="reply.suspended" :rep="reply" @update:suspend="updateSuspendButton")
.ml-9.mb-9.py-7.flex.flex-wrap.gap-2
.col-md-10.px-2.mt-2
- ActionButton(:disabled="disableCriteria" class="sm:my-2 transition duration-300 bg-orange-999 hover:bg-green-600 disabled:bg-orange-800 disabled:cursor-not-allowed" @click="save") Save
+ ActionButton(:class="{'transition duration-300 bg-orange-999 hover:bg-green-600': true, 'cursor-not-allowed': disableSubmit }" :disabled="disableSubmit" @click="save") Save
.col-md-10.py-2.mt-2
LinkButton(class="sm:my-2 transition duration-300 bg-orange-999 hover:bg-green-600" v-if="pageCuid!=0" :to="`/Page/${cuid}`") View Page
.col-md-10.p-2.pt-6.mt-2(class="sm:pt-2 sm:ml-auto sm:mr-6")
diff --git a/pages/EditUser/[id].vue b/pages/EditUser/[id].vue
index 99c87be2..e9bfd1e7 100644
--- a/pages/EditUser/[id].vue
+++ b/pages/EditUser/[id].vue
@@ -67,7 +67,7 @@ const router = useRoute()
const isAuthorized = computed(() => { cvuser.value?.user_role as string == "advocate" || cvuser.value?.user_role == "admin"})
const isAdmin = computed(() => cvuser.value?.user_role as string == "admin")
const cuid = computed(() => router.params.id as string);
-const disableCriteria = computed(() => !data_user.value?.email || !data_user.value?.first_name ||!data_user.value?.last_name)
+const disableCriteria = computed(() => !data_user.value?.email || !data_user.value?.first_name ||!data_user.value?.last_name || (addingFamily.value && !familyCuid.value))
const errorInPage = ref(false);
const errorToUser = ref("")
diff --git a/pages/Page/[id].vue b/pages/Page/[id].vue
index b2c0feb6..e3221bfa 100644
--- a/pages/Page/[id].vue
+++ b/pages/Page/[id].vue
@@ -1,352 +1,369 @@
-
-
-
-// donation popup
-.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.bg-white(class="w-1/2")
- 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.align-center.justify-center(@click.self="commentModalOpen=false" v-if="commentModalOpen" style="z-index: 10; position: fixed; top: 0; bottom: 0; left: 0; right: 0; background: rgba(0, 0, 0, 0.7);")
- .mx-auto.flex.p-2.bg-white(class="w-1/2 h-1/2")
- CommentPopup(@ExitComment="exitCommentPopup" :comment="currentComment")
-// the header overlay with image and name
-.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-5xl {{ pageDataDB.page_first_name + " " + pageDataDB.page_last_name }}
-
-.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; 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; width: 46px; height: 46px; border-radius:50%; align-items: center; justify-content: center; line-height: 2; text-align: center;color: white;") >
- img.w-96.cursor-pointer(style="z-index: -1; object-fit: cover;" @click="showPreview(profileImage)" :src="imageData[currentImage].url")
- div(class="w-full h-full overflow-auto block fixed left-0 top-0" style="z-index: 1000; background-color: rgba(0, 0, 0, 0.8);" v-if="previewImage" @click.self="closePreview")
- img.py-2(v-for="(image, index) in imageData" class="block max-w-[80%] max-h-[80%] m-auto"
- :key="index" :src="image.url")
- span.absolute.cursor-pointer(class="top-[15px] right-[35px] text-[30px] transition duration-300 text-gray-400 hover:text-white focus:text-white cursor-pointer"
- @click="closePreview"
- ) ×
- // 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
- .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" }}
- .flex.row.gap-2
- .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 {{ "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) }}
- .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 }}
- .flex.row.gap-2
- .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.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")
- .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")
-.py-4.grid.gap-1(style="text-align: left")
- .py-4.grid.gap-4(v-if="comments?.length" class="w-full justify-center" style="grid-template-columns: repeat(3, 30rem); ")
- .flex.flex-col.gap-4.h-full.min-w-44.p-4.rounded-lg.bg-white.border-border-gray-300.shadow-md(v-for="(comment, i) in comments" :key="i" class="py-4 transition duration-300 hover:shadow-xl")
- .flex.justify-between.gap-5
- .text-xl.font-bold {{ comment.donorFirstName }} {{ comment.donorLastName }}
- .text-xl.font-bold {{ dateFormat(comment.donationDate, true) }}
- .text-xl.grow.w-fit.text-gray-600(:class="{'border-l-2 border-green-500 pl-3': comment.comments.length}") {{ comment.comments.length > 200 ? (comment.comments.substring(0, 200) + " ...") : comment.comments.substring(0, 200) }}
- span.text-xl.w-fit.text-green-400.cursor-pointer(v-if="comment.comments.length > 200" @click="openCommentPopup(comment.comments)") {{ " Read More" }}
- .flex.justify-between.gap-5
- .text-xl.font-bold Amount Donated
- .text-xl.font-bold.text-green-600 {{ donationFormat(comment.amount) }}
- CVReplySystem(:pageCuid="id" :familyCuid="familyCuid" :replies="replies" @displayReply="displayReply")
- .py-4.grid.row-span-3.gap-2(v-if="replies?.length")
- .p-2.bg-white.rounded-lg.mb-2.shadow-md.pb-4(v-for="(reply,i) in replies.filter(item => !item.suspended)" :key="i")
- .flex.justify-between.gap-5.pd-4
- .ml-1.text-lg.font-bold {{reply.name}}
- .ml-1.text-lg {{ dateFormat(reply.date) }}
- .ml-1.pt-3.pb-3.pl-5.border-l-2.border-green-500 {{reply.reply}}
-div.flex(style="color: gray; font-weight: 700; justify-content: center; align-items: center; height: 100px;")
- label SHARE THIS PAGE |
- .col
- button(@click="shareFacebook")
- img(src="/facebook-fa.png" style="width:30px; height:33px;")
- .col
- button(@click="shareXFormerlyKnownAsTwitter")
- img(src="/twitter_fa.png" style="width:30px; height:29px;")
- .col
- button(@click="shareMail")
- img(src="/mail_fa.png" style="width:50px; height:29px;")
- .col
- p {{ "" }}
-
+
+
+
+// donation popup
+.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.bg-white(class="w-1/2")
+ 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.align-center.justify-center(@click.self="commentModalOpen=false" v-if="commentModalOpen" style="z-index: 10; position: fixed; top: 0; bottom: 0; left: 0; right: 0; background: rgba(0, 0, 0, 0.7);")
+ .mx-auto.flex.p-2.bg-white(class="w-1/2 h-1/2")
+ CommentPopup(@ExitComment="exitCommentPopup" :comment="currentComment")
+// the header overlay with image and name
+.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-5xl {{ pageDataDB.page_first_name + " " + pageDataDB.page_last_name }}
+
+.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(v-if="pageDataDB.day_of_birth && pageDataDB.day_of_passing") {{ dateFormat(pageDataDB.day_of_birth, true) + ((pageDataDB.day_of_birth || pageDataDB.day_of_passing) ? ' - ' : '') + dateFormat(pageDataDB.day_of_passing, true) }}
+ .text-gray-dark.mx-auto.w-max.font-poppins.text-md(v-else) {{ "Age: " + pageDataDB.age }}
+ .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; 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; width: 46px; height: 46px; border-radius:50%; align-items: center; justify-content: center; line-height: 2; text-align: center;color: white;") >
+ img.w-96.cursor-pointer(style="z-index: -1; object-fit: cover;" @click="showPreview(profileImage)" :src="imageData[currentImage].url")
+ div(class="w-full h-full overflow-auto block fixed left-0 top-0" style="z-index: 1000; background-color: rgba(0, 0, 0, 0.8);" v-if="previewImage" @click.self="closePreview")
+ img.py-2(v-for="(image, index) in imageData" class="block max-w-[80%] max-h-[80%] m-auto"
+ :key="index" :src="image.url")
+ span.absolute.cursor-pointer(class="top-[15px] right-[35px] text-[30px] transition duration-300 text-gray-400 hover:text-white focus:text-white cursor-pointer"
+ @click="closePreview"
+ ) ×
+ // 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
+ .font-outfit.font-bold {{ "Time:" }}
+ .font-outfit.gap-y-5 {{ longDateFormat(pageDataDB.visitation_date, true) + " - " + longDateFormat(pageDataDB.visitation_end_time, true) }}
+ .flex.row.gap-2
+ .font-outfit.font-bold {{ "Location:" }}
+ .font-outfit.whitespace-normal {{ pageDataDB.visitation_location ? pageDataDB.visitation_location : "TBD" }}
+ .flex.row.gap-2
+ .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 {{ "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) }}
+ .flex.row.gap-2
+ .font-outfit.font-bold {{ "Time:" }}
+ .font-outfit.gap-y-5 {{ longDateFormat(pageDataDB.funeral_date, true) + " - " + longDateFormat(pageDataDB.funeral_end_time, true) }}
+ .flex.row.gap-2
+ .font-outfit.font-bold {{ "Location:" }}
+ .font-outfit.whitespace-normal {{ pageDataDB.funeral_location }}
+ .flex.row.gap-2
+ .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.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")
+ .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")
+.py-4.grid.gap-1(style="text-align: left")
+ .py-4.grid.gap-4(v-if="comments?.length" class="w-full justify-center" style="grid-template-columns: repeat(3, 30rem); ")
+ .flex.flex-col.gap-4.h-full.min-w-44.p-4.rounded-lg.bg-white.border-border-gray-300.shadow-md(v-for="(comment, i) in comments" :key="i" class="py-4 transition duration-300 hover:shadow-xl")
+ .flex.justify-between.gap-5
+ .text-xl.font-bold {{ comment.donorFirstName }} {{ comment.donorLastName }}
+ .text-xl.font-bold {{ dateFormat(comment.donationDate, true) }}
+ .text-xl.grow.w-fit.text-gray-600(:class="{'border-l-2 border-green-500 pl-3': comment.comments.length}") {{ comment.comments.length > 200 ? (comment.comments.substring(0, 200) + " ...") : comment.comments.substring(0, 200) }}
+ span.text-xl.w-fit.text-green-400.cursor-pointer(v-if="comment.comments.length > 200" @click="openCommentPopup(comment.comments)") {{ " Read More" }}
+ .flex.justify-between.gap-5
+ .text-xl.font-bold Amount Donated
+ .text-xl.font-bold.text-green-600 {{ donationFormat(comment.amount) }}
+ CVReplySystem(:pageCuid="id" :familyCuid="familyCuid" :replies="replies" @displayReply="displayReply")
+ .py-4.grid.row-span-3.gap-2(v-if="replies?.length")
+ .p-2.bg-white.rounded-lg.mb-2.shadow-md.pb-4(v-for="(reply,i) in replies.filter(item => !item.suspended)" :key="i")
+ .flex.justify-between.gap-5.pd-4
+ .ml-1.text-lg.font-bold {{reply.name}}
+ .ml-1.text-lg {{ dateFormat(reply.date) }}
+ .ml-1.pt-3.pb-3.pl-5.border-l-2.border-green-500 {{reply.reply}}
+div.flex(style="color: gray; font-weight: 700; justify-content: center; align-items: center; height: 100px;")
+ label SHARE THIS PAGE |
+ .col
+ button(@click="shareFacebook")
+ img(src="/facebook-fa.png" style="width:30px; height:33px;")
+ .col
+ button(@click="shareXFormerlyKnownAsTwitter")
+ img(src="/twitter_fa.png" style="width:30px; height:29px;")
+ .col
+ button(@click="shareMail")
+ img(src="/mail_fa.png" style="width:50px; height:29px;")
+ .col
+ p {{ "" }}
+
\ No newline at end of file
diff --git a/pages/PageList/[id].vue b/pages/PageList/[id].vue
index fa9df244..5bda0dcb 100644
--- a/pages/PageList/[id].vue
+++ b/pages/PageList/[id].vue
@@ -61,10 +61,8 @@ const family_cuid_data = computed(() => router.params.id)
family_cuid.value = family_cuid_data.value as string;
const user_cuid_data = computed(() => router.params.id)
const user_cuid = user_cuid_data.value as string
-//const pages = ref([])
-//const pages2 = ref([])
+
const cvuser = useCookie('cvuser')
-const user_cuid2 = cvuser.value.cuid
const isAdmin = computed(() => cvuser.value?.user_role == "admin")
const isAdvocate = computed(() => cvuser.value?.user_role == "advocate");
const currentPage = ref(0)
@@ -72,15 +70,40 @@ const currentPage = ref(0)
const data = ref({
cuid: "",
+ isActive: true,
first_name: "",
last_name: "",
user_role: "",
email: "",
middle_name: "",
phone: "",
- address: "",
Pages: [],
familyCuid: "",
+ Family: {
+ cuid: "",
+ family_name: "",
+ stripe_account_id: "",
+ created_at: "",
+ updated_at: "",
+ advocateCuid: "",
+ FamilyMembers: [],
+ FamilyDonationPayouts: [],
+ Pages: [],
+ AdvocateResponsible: {
+ cuid: "",
+ isActive: true,
+ first_name: "",
+ last_name: "",
+ Family: {},
+ user_role: '',
+ email: '',
+ middle_name: '',
+ phone: '',
+ Pages: [],
+ familyCuid: '',
+ AdvocateFamily: []
+ },
+ },
AdvocateFamily: []
})
@@ -93,11 +116,22 @@ const data_families = ref([])
const fromUser = computed(() => router.query.fromUsers as string == '1')
const entityCuid = computed(() => fromUser.value ? user_cuid : familyCuid.value)
-// Method to populate the page list with data based on the cuid of the user in the url
+// Please do not remove, used for selecting all families again
+const data_family = ({
+ cuid: '',
+ family_name: '-',
+ stripe_account_id: null,
+ created_at: new Date(),
+ updated_at: new Date(),
+ advocateCuid: '',
+ })
+
+// Method to all pages for admins and assigned families for advocates
const getDataAdminAdvocate = async () => {
if(cvuser.value.user_role == "admin") {
const { data: all_families } = await useFetch('/api/family', {
method: 'GET',
+ transform: (data: Family[]) => [{family_name: "-", cuid: ""}, ...data],
default() {
return [] as any
}
@@ -105,9 +139,8 @@ const getDataAdminAdvocate = async () => {
data_families.value = all_families.value as unknown as Family[]
} else if(cvuser.value.user_role == "advocate"){
// extracting the families that the advocate is responsible for
- const { data: advocateFamilies } = await useFetch('/api/user', {
+ const { data: advocateFamilies } = await useFetch(`/api/user/${user_cuid}`, {
method: 'GET',
- query: { cuid: user_cuid },
default() {
return [] as any
}
@@ -115,8 +148,12 @@ const getDataAdminAdvocate = async () => {
data_families.value = advocateFamilies.value?.AdvocateFamily as unknown as Family[]
}
+ if(data_families.value[0]?.family_name !== "-") {
+ data_families.value.unshift(data_family)
+ }
}
+
// handles changes of family selected for advocates or admins
// handles retrieving all of a family's pages for families
// handles retrieving all of a user's pages if coming from the user list
@@ -133,35 +170,10 @@ const totalLength = computed(() => pages.value?.Pagination?.total as unknown as
watch(familyCuid, () => {
currentPage.value = 0
})
-/*if(cvuser.value?.user_role == 'family' || fromUser.value) {
- pages.value = (family_pages.value.data) as unknown as Page2[]
-}
-watch(family_pages, () => {
- console.log("here", entityCuid)
- pages.value = (family_pages.value.data) as unknown as Page2[]
- totalLength.value = totalLength2.value
-})*/
- // Todo: Talk to Taz about this
- /*
- // For winter clean up
- prisma.page.FindMany({
- where: {
- Family: {
- User: { cuid: body.cuid}
- }
- }
- })
- */
-//pages.value = family_pages.value as unknown as Page[]
-//watchEffect(() => familyCuid.value = data_families.value![0]?.cuid || "");
-//pages.value = familyData.value?.Pages as unknown as Page[]
-// one main endpoint for maintainability
const nextPage = () => {
if(currentPage.value < Math.max(((totalLength.value / 12) - 1), 0)) {//, ((totalLength2.value / 12) - 1))){
currentPage.value++
- //if(fromUser.value || cvuser.value.user_role == "family" || isAdmin)
- //getDataPageList()
}
}
@@ -177,7 +189,7 @@ const goToPage = (pageNum: number) => {
console.log(pageNum)
if(pageNum >= 0 && pageNum < ((totalLength.value / 12))) {
currentPage.value = pageNum
- }
+ }
}
@@ -187,22 +199,22 @@ await getDataAdminAdvocate()
-
-button(type="button" class="ml-4 my-4 text-white px-4 py-2 rounded-full w-32 transition duration-300 bg-orange-999 hover:bg-green-600" @click="tableToggle = !tableToggle") {{ tableToggle ? "all pages" : "archive"}}
-.py-4.grid(class="sm:grid-cols-3" v-if="(isAdmin || isAdvocate) && !fromUser")
- CVLabel Current Family
- .col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
- Listbox.shadow-sm.border.border-1.rounded-lg(v-if="isAdmin || isAdvocate" as='div' v-model="familyCuid")
- .relative
- Transition(
- leave-active-class='transition ease-in duration-100'
- leave-from-class='opacity-100'
- leave-to-class='opacity-0'
- )
- ListboxOptions(as='div' class='w-full absolute z-10 mt-10 bg-white shadow-lg max-h-60 rounded-md px-2 py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm' )
- ListboxOption(as='div' v-for="family in data_families" :key="family.cuid" :value="family.cuid" class="px-2 border border-grey-500 py-1 my-1") {{ family.family_name }}
- ListboxButton(class='text-left bg-white relative rounded-md pl-2 pr-10 py-2 sm:text-sm w-96') {{ familyCuid ? currentFamily.family_name : 'Select family to view pages from' }}
-.mx-auto.mt-1(class="sm:w-[1200px]")
+.flex.gap-5.w-full.justify-center
+ button(type="button" class="ml-4 my-4 text-white px-4 py-2 rounded-full w-32 transition duration-300 bg-orange-999 hover:bg-green-600" @click="tableToggle = !tableToggle") {{ tableToggle ? "All Pages" : "Archive"}}
+ .py-4.grid(class="sm:grid-cols-3" v-if="(isAdmin || isAdvocate) && !fromUser")
+ CVLabel Current Family
+ .col-md-8.mx-9(class="sm:col-span-2 sm:mr-11")
+ Listbox.shadow-sm.border.border-1.rounded-lg(v-if="isAdmin || isAdvocate" as='div' v-model="familyCuid")
+ .relative
+ Transition(
+ leave-active-class='transition ease-in duration-100'
+ leave-from-class='opacity-100'
+ leave-to-class='opacity-0'
+ )
+ ListboxOptions(as='div' class='w-full absolute z-10 mt-10 bg-white shadow-lg max-h-60 rounded-md px-2 py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm' )
+ ListboxOption(as='div' v-for="family in data_families" :key="family.cuid" :value="family.cuid" class="px-2 border border-grey-500 py-1 my-1") {{ family.family_name }}
+ ListboxButton(class='text-left bg-white relative rounded-md pl-2 pr-10 py-2 sm:text-sm w-96') {{ familyCuid ? currentFamily.family_name : 'Select family to view pages from' }}
+.mx-auto.mt-1(class="w-11/12 sm:w-[1200px]")
table(style="table-layout: auto;")
thead
tr
@@ -219,14 +231,14 @@ button(type="button" class="ml-4 my-4 text-white px-4 py-2 rounded-full w-32 tra
tbody
tr(v-for="(item, i) in ( tableToggle ? pages.data.filter(item => item.status == 'active') : pages.data)" :class="{'bg-gray-200': (i + 1) % 2}")
- td.font-poppins.text-gray-dark.font-bold.text-center {{ item.page_first_name + " " + item.page_last_name }}
- td.font-poppins.text-gray-dark.font-bold.text-center {{ item.User?.first_name + " " + item.User?.last_name }}
- td.font-poppins.text-gray-dark.font-bold.text-center {{ item.Family?.AdvocateResponsible ? (item.Family?.AdvocateResponsible?.first_name + " " + item.Family?.AdvocateResponsible?.last_name) : 'No Advocate assigned'}}
- td.font-poppins.text-gray-dark.font-bold.text-center {{ item.Family?.family_name }}
- td.font-poppins.text-gray-dark.font-bold.text-center {{ donationFormat(item.amount_raised) }}
- td.font-poppins.text-gray-dark.font-bold.text-center {{ dateFormat(item.start_date) }}
- td.font-poppins.text-gray-dark.font-bold.text-center {{ dateFormat(item.deadline) }}
- td.font-poppins.text-gray-dark.font-bold.text-center {{ donationFormat(item.donation_goal) }}
+ td.font-poppins.text-gray-dark.font-bold.text-center {{ item?.page_first_name + " " + item?.page_last_name }}
+ td.font-poppins.text-gray-dark.font-bold.text-center {{ item?.User?.first_name + " " + item?.User?.last_name }}
+ td.font-poppins.text-gray-dark.font-bold.text-center {{ item?.Family?.AdvocateResponsible ? (item.Family?.AdvocateResponsible?.first_name + " " + item?.Family?.AdvocateResponsible?.last_name) : 'No Advocate assigned'}}
+ td.font-poppins.text-gray-dark.font-bold.text-center {{ item?.Family?.family_name }}
+ td.font-poppins.text-gray-dark.font-bold.text-center {{ donationFormat(item?.amount_raised) }}
+ td.font-poppins.text-gray-dark.font-bold.text-center {{ dateFormat(item?.start_date) }}
+ td.font-poppins.text-gray-dark.font-bold.text-center {{ dateFormat(item?.deadline) }}
+ td.font-poppins.text-gray-dark.font-bold.text-center {{ donationFormat(item?.donation_goal) }}
td
LinkButton(class="sm:my-2 transition duration-300 bg-orange-999 hover:bg-green-600" style="white-space: nowrap; display: flex; flex-direction: row; padding: 14px 24px; gap: 10px;" :to="`/EditPage/${item.cuid}`") Edit
td
diff --git a/pages/Users.vue b/pages/Users.vue
index b3575558..0ad36a30 100644
--- a/pages/Users.vue
+++ b/pages/Users.vue
@@ -1,148 +1,248 @@
-
-
-
-.container.bg-white.mx-auto.mt-1(class="max-w-[1100px]" )
- table(style="table-layout: auto;")
- thead
- tr
- th.font-poppins.font-bold.font-bold.p-2(style="color: white;--tw-bg-opacity: 1; background-color: #5aadc2;border-radius: 60px 0px 0px 0px; width:25%; overflow: hidden")
- button(@click="SortCV(users, 'last_name')") User Name
- span(v-if="order === 'asc' && OrderField==='last_name'" style="padding-right:3px; padding-top: 3px;")
- ChevronUpIcon.h-6.inline-flex
- span(v-else-if="order === 'desc' && OrderField==='last_name'" style="padding-right:3px; padding-top: 3px;")
- ChevronDownIcon.h-6.inline-flex
- span(v-else style="padding-right:3px; padding-top: 3px;")
- ChevronUpDownIcon.h-6.inline-flex
- th.font-poppins.font-bold(style="color: white; width: 25%; --tw-bg-opacity: 1; background-color: #5aadc2;")
- button(@click="SortCV(users, 'family_name')") Family Name
- span(v-if="order === 'asc' && OrderField==='family_name'" style="padding-right:3px; padding-top: 3px;")
- ChevronUpIcon.h-6.inline-flex
- span(v-else-if="order === 'desc' && OrderField==='family_name'" style="padding-right:3px; padding-top: 3px;")
- ChevronDownIcon.h-6.inline-flex
- span(v-else style="padding-right:3px; padding-top: 3px;")
- ChevronUpDownIcon.h-6.inline-flex
- th.font-poppins.font-bold(style="color: white; width: 12.5%; --tw-bg-opacity: 1; background-color: #5aadc2;")
- button(@click="SortCV(users, 'user_role')") User Role
- span(v-if="order === 'asc' && OrderField==='user_role'" style="padding-right:3px; padding-top: 3px;")
- ChevronUpIcon.h-6.inline-flex
- span(v-else-if="order === 'desc' && OrderField==='user_role'" style="padding-right:3px; padding-top: 3px;")
- ChevronDownIcon.h-6.inline-flex
- span(v-else style="padding-right:3px; padding-top: 3px;")
- ChevronUpDownIcon.h-6.inline-flex
- th.font-poppins.font-bold(style="color: white; --tw-bg-opacity: 1; background-color: #5aadc2;")
- button(@click="SortCV(users, 'email')") User Email
- span(v-if="order === 'asc' && OrderField==='email'" style="padding-right:3px; padding-top: 3px;")
- ChevronUpIcon.h-6.inline-flex
- span(v-else-if="order === 'desc' && OrderField==='email'" style="padding-right:3px; padding-top: 3px;")
- ChevronDownIcon.h-6.inline-flex
- span(v-else style="padding-right:3px; padding-top: 3px;")
- ChevronUpDownIcon.h-6.inline-flex
- th.font-poppins.font-bold(style="width:25%; --tw-bg-opacity: 1; background-color: #5aadc2; color: white;") {{ "User Editor" }}
- th.font-poppins.font-bold(style="width:25%; --tw-bg-opacity: 1; border-radius: 0px 60px 0px 0px; background-color: #5aadc2; color: white;") {{ "User Pages" }}
- tr(v-for="(item, i) in users"
- :key="i"
- :class="{'bg-gray-200': (i+1) % 2}"
- )
- td.font-poppins.text-gray-dark.font-bold(style="text-align: center") {{ item.first_name + " " + item.last_name }}
- td.font-poppins.text-gray-dark.font-bold(style="text-align: center") {{ item.Family?.family_name }}
- td.font-poppins.text-gray-dark.font-bold(style="text-align: center") {{ item.user_role }}
- td.font-poppins.text-gray-dark.font-bold(style="text-align: center") {{ item.email }}
- td
- LinkButton(class="sm:my-2 transition duration-300 bg-orange-999 hover:bg-green-600" style="--tw-bg-opacity: 1; white-space: nowrap; display: flex; flex-direction: row; padding: 14px 24px; gap: 10px;" :to="`/EditUser/${item.cuid}`") Edit
-
- td
- LinkButton(class="sm:my-2 transition duration-300 bg-orange-999 hover:bg-green-600" style="--tw-bg-opacity: 1; white-space: nowrap; display: flex; flex-direction: row; padding: 14px 24px; gap: 10px;" :to="`/pageList/${item.cuid}?fromUsers=1`") View
- .container.mx-auto(class="w-full max-w-[1100px]" style="--tw-bg-opacity: 1; background-color: #5aadc2; height: 50px; border-radius: 0px 0px 60px 60px;")
-.mb-9.py-7.flex.flex-wrap.gap-2.place-content-center
- .col-md-10.px-2.mt-2
- button(@click="prevPage") <
- .col-md-10.px-2.mt-2
- p {{ currentPage + 1 }}
- .col-md-10.px-2.mt-2
- button(@click="nextPage") >
-
+
+
+
+.flex.w-full.justify-center
+ button(type="button" class="ml-4 my-4 text-white px-4 py-2 font-bold rounded-full w-32 transition duration-300 bg-orange-999 hover:bg-green-600" @click="tableToggle = !tableToggle") {{ tableToggle ? "All Users" : "Archive"}}
+ .py-4.grid(class="sm:grid-cols-3" v-if="(isAuthorized)")
+ CVLabel Current Family
+ .col-md-8.mx-9(class="sm:col-span-1 sm:mr-11")
+ Listbox.shadow-sm.border.border-1.rounded-lg(v-if="isAuthorized" as='div' v-model="familyCuid")
+ .relative
+ Transition(
+ leave-active-class='transition ease-in duration-100'
+ leave-from-class='opacity-100'
+ leave-to-class='opacity-0'
+ )
+ ListboxOptions(as='div' class='w-full absolute z-10 mt-10 bg-white shadow-lg max-h-60 rounded-md px-2 py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm' )
+ ListboxOption(as='div' v-for="family in all_families" :key="family.cuid" :value="family.cuid" class="px-2 border border-grey-500 py-1 my-1") {{ family.family_name }}
+ ListboxButton(class='text-left bg-white relative rounded-md pl-2 pr-10 py-2 sm:text-sm w-96') {{ familyCuid ? currentFamily.family_name : 'Select family to view users from' }}
+.container.bg-white.mx-auto.mt-1(class="w-11/12 sm:w-[1500px]" )
+ table(style="table-layout: auto;")
+ thead
+ tr.text-white
+ th.font-poppins.font-bold.cursor-pointer.p-2(style="color: white;background-color: #5aadc2; border-radius: 60px 0px 0px 0px; width:25%; overflow: hidden" @click="SortCV(users, 'last_name')")
+ label User Name
+ span(v-if="order === 'asc' && OrderField==='last_name'" style="padding-right:3px; padding-top: 3px;")
+ ChevronUpIcon.h-6.inline-flex
+ span(v-else-if="order === 'desc' && OrderField==='last_name'" style="padding-right:3px; padding-top: 3px;")
+ ChevronDownIcon.h-6.inline-flex
+ span(v-else style="padding-right:3px; padding-top: 3px;")
+ ChevronUpDownIcon.h-6.inline-flex
+ th.font-poppins.font-bold.cursor-pointer(style="width: 25%; background-color: #5aadc2;" @click="SortCV(users, 'family_name')")
+ label Family Name
+ span(v-if="order === 'asc' && OrderField==='family_name'" style="padding-right:3px; padding-top: 3px;")
+ ChevronUpIcon.h-6.inline-flex
+ span(v-else-if="order === 'desc' && OrderField==='family_name'" style="padding-right:3px; padding-top: 3px;")
+ ChevronDownIcon.h-6.inline-flex
+ span(v-else style="padding-right:3px; padding-top: 3px;")
+ ChevronUpDownIcon.h-6.inline-flex
+ th.font-poppins.font-bold.cursor-pointer(style="width: 15%; background-color: #5aadc2;" @click="SortCV(users, 'user_role')")
+ label User Role
+ span(v-if="order === 'asc' && OrderField==='user_role'" style="padding-right:3px; padding-top: 3px;")
+ ChevronUpIcon.h-6.inline-flex
+ span(v-else-if="order === 'desc' && OrderField==='user_role'" style="padding-right:3px; padding-top: 3px;")
+ ChevronDownIcon.h-6.inline-flex
+ span(v-else style="padding-right:3px; padding-top: 3px;")
+ ChevronUpDownIcon.h-6.inline-flex
+ th.font-poppins.font-bold.cursor-pointer(style="width: 15%; background-color: #5aadc2;" @click="SortCV(users, '_count')")
+ label Pages Created
+ span(v-if="order === 'asc' && OrderField==='_count'" style="padding-right:3px; padding-top: 3px;")
+ ChevronUpIcon.h-6.inline-flex
+ span(v-else-if="order === 'desc' && OrderField==='_count'" style="padding-right:3px; padding-top: 3px;")
+ ChevronDownIcon.h-6.inline-flex
+ span(v-else style="padding-right:3px; padding-top: 3px;")
+ ChevronUpDownIcon.h-6.inline-flex
+ th.font-poppins.font-bold.cursor-pointer(style="width: 12%; background-color: #5aadc2;" @click="SortCV(users, 'email')")
+ label User Email
+ span(v-if="order === 'asc' && OrderField==='email'" style="padding-right:3px; padding-top: 3px;")
+ ChevronUpIcon.h-6.inline-flex
+ span(v-else-if="order === 'desc' && OrderField==='email'" style="padding-right:3px; padding-top: 3px;")
+ ChevronDownIcon.h-6.inline-flex
+ span(v-else style="padding-right:3px; padding-top: 3px;")
+ ChevronUpDownIcon.h-6.inline-flex
+ th.font-poppins.font-bold.cursor-pointer(style="width: 10%; background-color: #5aadc2; color: white;" v-if="isAdmin" @click="SortCV(users, 'isActive')")
+ label User Status
+ span(v-if="order === 'asc' && OrderField==='isActive'" style="padding-right:3px; padding-top: 3px;")
+ ChevronUpIcon.h-6.inline-flex
+ span(v-else-if="order === 'desc' && OrderField==='isActive'" style="padding-right:3px; padding-top: 3px;")
+ ChevronDownIcon.h-6.inline-flex
+ span(v-else style="padding-right:3px; padding-top: 3px;")
+ ChevronUpDownIcon.h-6.inline-flex
+ th.font-poppins.font-bold(style="width: 25%; background-color: #5aadc2; color: white;") {{ "User Editor" }}
+ th.font-poppins.font-bold(style="width: 25%; border-radius: 0px 60px 0px 0px; background-color: #5aadc2; color: white;") {{ "Pages" }}
+ tr(v-for="(item, i) in (tableToggle ? users.filter(item => item.isActive) : users)"
+ :key="i"
+ :class="{'bg-gray-200': (i+1) % 2}"
+ )
+ td.font-poppins.text-gray-dark.font-bold.text-center {{ item.first_name + " " + item.last_name }}
+ td.font-poppins.text-gray-dark.font-bold.text-center {{ item.Family?.family_name }}
+ td.font-poppins.text-gray-dark.font-bold.text-center {{ item.user_role }}
+ td.font-poppins.text-gray-dark.font-bold.text-center {{ item.Pages.length }}
+ td.font-poppins.text-gray-dark.font-bold.text-center {{ item.email }}
+ td
+ button.text-white.font-poppins.font-bold.rounded-full(class="sm:my-2 transition" style="white-space: nowrap; display: flex; flex-direction: row; padding: 14px 24px; gap: 10px; background-color: red;" @click="ArchiveUser(item)" v-if="isAdmin") {{ item.isActive ? "Archive" : "Reactivate" }}
+ td
+ LinkButton(class="sm:my-2 transition duration-300 bg-orange-999 hover:bg-green-600" style="white-space: nowrap; display: flex; flex-direction: row; padding: 14px 24px; gap: 10px;" :to="`/EditUser/${item.cuid}`") Edit
+
+ td
+ LinkButton(class="sm:my-2 transition duration-300 bg-orange-999 hover:bg-green-600" style="--tw-bg-opacity: 1; white-space: nowrap; display: flex; flex-direction: row; padding: 14px 24px; gap: 10px;" :to="`/pageList/${item.cuid}?fromUsers=1`") View
+ .container.mx-auto(class="w-full max-w-[1500px]" style="--tw-bg-opacity: 1; background-color: #5aadc2; height: 50px; border-radius: 0px 0px 60px 60px;")
+.mb-9.py-7.flex.flex-wrap.gap-2.place-content-center
+ .col-md-10.px-2.mt-2
+ button(@click="prevPage") <
+ .col-md-10.px-2.mt-2
+ p {{ currentPage + 1 }}
+ .col-md-10.px-2.mt-2
+ button(@click="nextPage") >
+
diff --git a/prisma/migrations/20250127203230_age/migration.sql b/prisma/migrations/20250127203230_age/migration.sql
new file mode 100644
index 00000000..86e974ff
--- /dev/null
+++ b/prisma/migrations/20250127203230_age/migration.sql
@@ -0,0 +1,2 @@
+-- AlterTable
+ALTER TABLE "pages" ADD COLUMN "age" INTEGER DEFAULT 0;
diff --git a/prisma/migrations/20250209163242_event_times/migration.sql b/prisma/migrations/20250209163242_event_times/migration.sql
new file mode 100644
index 00000000..addc520a
--- /dev/null
+++ b/prisma/migrations/20250209163242_event_times/migration.sql
@@ -0,0 +1,3 @@
+-- AlterTable
+ALTER TABLE "pages" ADD COLUMN "funeral_end_time" TIMESTAMP(3),
+ADD COLUMN "visitation_end_time" TIMESTAMP(3);
diff --git a/prisma/migrations/20250212234812_reassignable_advocates/migration.sql b/prisma/migrations/20250212234812_reassignable_advocates/migration.sql
new file mode 100644
index 00000000..16720b0c
--- /dev/null
+++ b/prisma/migrations/20250212234812_reassignable_advocates/migration.sql
@@ -0,0 +1,2 @@
+-- AlterTable
+ALTER TABLE "pages" ALTER COLUMN "donation_status" SET DEFAULT 'In progress';
diff --git a/prisma/migrations/20250212235241_reassignable_advocates_cuid/migration.sql b/prisma/migrations/20250212235241_reassignable_advocates_cuid/migration.sql
new file mode 100644
index 00000000..1b98b655
--- /dev/null
+++ b/prisma/migrations/20250212235241_reassignable_advocates_cuid/migration.sql
@@ -0,0 +1,8 @@
+-- DropForeignKey
+ALTER TABLE "Family" DROP CONSTRAINT "Family_advocate_cuid_fkey";
+
+-- AlterTable
+ALTER TABLE "Family" ALTER COLUMN "advocate_cuid" DROP NOT NULL;
+
+-- AddForeignKey
+ALTER TABLE "Family" ADD CONSTRAINT "Family_advocate_cuid_fkey" FOREIGN KEY ("advocate_cuid") REFERENCES "user_accounts"("cuid") ON DELETE SET NULL ON UPDATE CASCADE;
diff --git a/prisma/migrations/20250224032819_user_status/migration.sql b/prisma/migrations/20250224032819_user_status/migration.sql
new file mode 100644
index 00000000..9bb57d04
--- /dev/null
+++ b/prisma/migrations/20250224032819_user_status/migration.sql
@@ -0,0 +1,2 @@
+-- AlterTable
+ALTER TABLE "user_accounts" ADD COLUMN "isActive" BOOLEAN NOT NULL DEFAULT true;
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 413218d2..fe18bafe 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -10,6 +10,7 @@ datasource db {
model User {
cuid String @id @default(cuid())
+ isActive Boolean @default(true)
email String @unique
user_role String @default("family")
first_name String @default("")
@@ -21,7 +22,6 @@ model User {
AdvocateFamily Family[] @relation("advocate_family")
Pages Page[]
Family Family? @relation("family_member", fields: [familyCuid], references: [cuid])
-
@@map("user_accounts")
}
@@ -31,9 +31,9 @@ model Family {
stripe_account_id String?
created_at DateTime
updated_at DateTime?
- advocateCuid String @map("advocate_cuid")
+ advocateCuid String? @map("advocate_cuid")
FamilyDonationPayouts DonationPayout[]
- AdvocateResponsible User @relation("advocate_family", fields: [advocateCuid], references: [cuid])
+ AdvocateResponsible User? @relation("advocate_family", fields: [advocateCuid], references: [cuid])
FamilyDonations PageDonation[]
Pages Page[]
FamilyMembers User[] @relation("family_member")
@@ -54,11 +54,14 @@ model Page {
cuid String @id @default(cuid())
day_of_birth DateTime?
day_of_passing DateTime?
+ age Int? @default(0)
visitation_date DateTime?
visitation_location String
visitation_address String @default("")
visitation_description String
+ visitation_end_time DateTime?
funeral_date DateTime?
+ funeral_end_time DateTime?
funeral_location String
funeral_description String
funeral_address String @default("")
@@ -72,7 +75,7 @@ model Page {
profileImageCuid String @map("profile_image_cuid")
familyCuid String? @map("family_cuid")
status String @default("active")
- donation_status String @default("in progress")
+ donation_status String @default("In progress")
duration String @default("0 days")
start_date DateTime
goal_met_date DateTime?
diff --git a/server/api/image.delete.ts b/server/api/image.delete.ts
index 3d46f02d..d05ab85d 100644
--- a/server/api/image.delete.ts
+++ b/server/api/image.delete.ts
@@ -1,6 +1,5 @@
import {loginRedirectUrl} from "../api/auth0"
-import type { Page } from "@/types.d.ts"
-
+import type { Page } from "@prisma/client"
/*
* /EditPage/cuid
* file: /Pages/EditPage.vue
@@ -11,7 +10,15 @@ import type { Page } from "@/types.d.ts"
export default defineEventHandler(async event => {
const body = await readBody(event);
- const pageFound = event.context.user?.Family?.Pages?.find((page:Page) => page.cuid == body.pageCuid) || undefined
+ const family = await event.context.client.family.findFirst({
+ where: { cuid: event.context.user?.familyCuid as string },
+ include: {
+ Pages: true,
+ }
+ });
+
+ const pageFound = family?.Pages.find(({ cuid }: Page) => cuid == body.pageCuid || {})
+ // todo check here instead of the event context object as it explodes the cookie
try {
if(event.context.user?.user_role === "advocate" || event.context.user?.user_role == "admin" || event.context.user?.user_role == 'family' && body.pageCuid == null || pageFound) {
// Deletes an image from the database.
diff --git a/server/api/user/[id].delete.ts b/server/api/user/[id].delete.ts
new file mode 100644
index 00000000..6cdf0eee
--- /dev/null
+++ b/server/api/user/[id].delete.ts
@@ -0,0 +1,29 @@
+import {logoutRedirectUrl} from "../../api/auth0"
+import type { Family } from "@/types.d.ts"
+/* /EditUser/cuid
+* function: PUT
+* submit user account details to database
+*/
+
+export default defineEventHandler(async event => {
+const id = getRouterParam(event, 'id')
+const body = await readBody(event);
+const cvtoken = getCookie(event, "cvtoken") || ""
+if(event.context.user?.user_role == "advocate" || event.context.user?.user_role === "admin"){
+ if(body.user_role == 'family' || (body.user_role == 'admin' && event.context.user?.user_role === "admin") || (body.user_role == 'advocate' && event.context.user?.user_role === "admin")) {
+ const queryRes = await event.context.client.user.update({
+ where: {
+ cuid: id
+ },
+ data: {
+ isActive: false
+ }
+ });
+ return true
+ }
+
+ return false
+} else {
+ return await sendRedirect(event, logoutRedirectUrl(cvtoken));
+}
+})
diff --git a/server/api/user.get.ts b/server/api/user/[id].get.ts
similarity index 67%
rename from server/api/user.get.ts
rename to server/api/user/[id].get.ts
index 0c4c5e1b..376dceba 100644
--- a/server/api/user.get.ts
+++ b/server/api/user/[id].get.ts
@@ -1,4 +1,4 @@
-import {loginRedirectUrl} from "../api/auth0"
+import {loginRedirectUrl} from "../../api/auth0"
/*
* /EditUser/cuid
@@ -7,16 +7,19 @@ import {loginRedirectUrl} from "../api/auth0"
*/
export default defineEventHandler(async event => {
- const { cuid } = getQuery(event);
- if( (cuid as string) == "0" || cuid == undefined){
+ const id = getRouterParam(event, "id");
+
+ if( (id as string) == "0" || id == undefined){
return []
}
+
// retrieves a single user
if(event.context.user?.user_role === "advocate" || event.context.user?.user_role === "admin") {
const queryRes = await event.context.client.user.findFirst({
- where: { cuid: (cuid as string) },
+ where: { cuid: (id as string) },
include: {
- AdvocateFamily: true
+ AdvocateFamily: true,
+ Family: true
}
});
return queryRes;
diff --git a/server/middleware/auth.ts b/server/middleware/auth.ts
index cbc94475..8fbcb728 100644
--- a/server/middleware/auth.ts
+++ b/server/middleware/auth.ts
@@ -24,16 +24,18 @@ export default defineEventHandler(async event => {
fs.readFileSync(process.cwd()+"/cert-dev.pem")
)
event.context.claims = claims
+ console.log(claims.email)
event.context.user = await event.context.client.user.findFirst(
{
where: { email: claims.email }
,
include: {
- Pages: {
+ /*Pages: {
select: {
cuid: true
}
- }, Family: {
+ }, */
+ Family: {
select: {
cuid: true,
stripe_account_id: true,
@@ -46,6 +48,7 @@ export default defineEventHandler(async event => {
}
}
}) as unknown as Partial & { Family: Partial}
+ console.log(event.context.user)
if(!event.context.user) {
console.error(`${claims.email} not found`)
setCookie(event,'cvtoken','')
diff --git a/types.d.ts b/types.d.ts
index 346c9be5..8c149249 100644
--- a/types.d.ts
+++ b/types.d.ts
@@ -1,169 +1,174 @@
-/*import {
- Family as PFamily,
- User as PUser,
- Page as PPage,
- Image as PImage,
- PageDonation as PPageDonation,
- DonationPayout as PDonationPayout,
- DonationToPage as PDonationToPage,
- FamilyToPage as PFamilyToPage,
- UserToFamily as PUserToFamily,
- UserToPage as PUserToPage,
- ImageToPage as PImage
-} from '@/prisma/client';
-import { DonationPayout } from '@prisma/client';
-
-export type Family = PFamily & {
- Pages?: FamilyToPage;
- FamilyMembers?: UserToFamily[];
- PageDonations?: PageDonation[];
- DonationPayout?: DonationPayout[];
-}
-
-export type User = PUser & {
- Pages?: UserToPage;
- PageDonations?: PageDonation[];
- DonationPayout?: DonationPayout[];
-};
-
-export type Page = PPage & {
- Pages?: UserToPage;
- PageDonations?: PageDonation[];
- DonationPayout?: DonationPayout[];
-};
-
-export type Image = PImage & {
- Pages?: ImageToPage;
-};
-
-export type PageDonation = PPageDonation & {
- Pages?: DonationToPage;
-}
-
-export type DonationPayout = PDonationPayout & {
- Pages?: PayoutToPage;
-};
-*/
-import { PrismaClient, DonationPayout } from "@prisma/client"
-
-declare module 'h3' {
- interface H3EventContext {
- user?: Partial & { Family: Partial};
- client: PrismaClient;
- claims?: {
- email: string
- }
- }
-}
-
-// TODO: import types from prisma, export them with relations added
-export type Page = {
- page_first_name: string,
- page_last_name: string,
- cuid: string,
- userCuid: string,
- day_of_birth: Date | string | null,
- 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
- Images: Image[],
- Family: Family,
- familyCuid: string,
- status: string,
- donation_status: string,
- duration: string,
- start_date: Date | string | null
- goal_met_date: Date | string | null
- PageDonations: PageDonation[]
- last_donation_date: Date | string | null
- Reply: Reply[]
- }
-
-export type Family = {
- cuid: string;
- family_name: string;
- stripe_account_id: string | null;
- created_at: Date | string | null;
- updated_at: Date | string | null;
- advocateCuid: string;
- Pages: Page[];
- FamilyMembers: User[];
- AdvocateResponsible: User;
- FamilyDonations: PageDonation[];
- FamilyDonationPayouts: DonationPayout[];
-}
-
-export type User = {
- cuid: string,
- first_name: string,
- last_name: string,
- user_role: string,
- email: string,
- address: string,
- middle_name: string,
- phone: string,
- Pages: Page[],
- familyCuid: string,
- AdvocateFamily: User[]
- //PageDonations: PageDonation[]
- //DonationPayouts: DonationPayout[]
-}
-
-export type PageDonation = {
- cuid: string,
- userCuid: string,
- familyCuid: String,
- pageCuid: string,
- success: boolean,
- transaction_id: string,
- amount: number,
- donorFirstName: string,
- donorLastName: string,
- donorEmail: string,
- isAnonymous: boolean,
- comments: string,
- Page: Page,
- donationDate: Date | string | null
- //User: User
-}
-
-
-export type donation_payout = {
- cuid: string,
- transaction_id: string,
- userCuid: string
- amount_to_record: number,
- transaction_recording_date: Date | string | null,
- familyCuid: string,
- page: Page
-}
-
-export type Image = {
- cuid: string
- url: string
- pageCuid: string
- //Page: Page
-}
-
-export type Reply = {
- cuid: string,
- pageCuid: string
- familyCuid: string
- name: string
- reply: string
- date: Date | string | null | undefined
- suspended: boolean
-}
+/*import {
+ Family as PFamily,
+ User as PUser,
+ Page as PPage,
+ Image as PImage,
+ PageDonation as PPageDonation,
+ DonationPayout as PDonationPayout,
+ DonationToPage as PDonationToPage,
+ FamilyToPage as PFamilyToPage,
+ UserToFamily as PUserToFamily,
+ UserToPage as PUserToPage,
+ ImageToPage as PImage
+} from '@/prisma/client';
+import { DonationPayout } from '@prisma/client';
+
+export type Family = PFamily & {
+ Pages?: FamilyToPage;
+ FamilyMembers?: UserToFamily[];
+ PageDonations?: PageDonation[];
+ DonationPayout?: DonationPayout[];
+}
+
+export type User = PUser & {
+ Pages?: UserToPage;
+ PageDonations?: PageDonation[];
+ DonationPayout?: DonationPayout[];
+};
+
+export type Page = PPage & {
+ Pages?: UserToPage;
+ PageDonations?: PageDonation[];
+ DonationPayout?: DonationPayout[];
+};
+
+export type Image = PImage & {
+ Pages?: ImageToPage;
+};
+
+export type PageDonation = PPageDonation & {
+ Pages?: DonationToPage;
+}
+
+export type DonationPayout = PDonationPayout & {
+ Pages?: PayoutToPage;
+};
+*/
+import { PrismaClient, DonationPayout } from "@prisma/client"
+
+declare module 'h3' {
+ interface H3EventContext {
+ user?: Partial & { Family: Partial};
+ client: PrismaClient;
+ claims?: {
+ email: string
+ }
+ }
+}
+
+// TODO: import types from prisma, export them with relations added
+export type Page = {
+ page_first_name: string,
+ page_last_name: string,
+ cuid: string,
+ userCuid: string,
+ day_of_birth: Date | string | null,
+ day_of_passing: Date | string | null,
+ age: number,
+ visitation_date: Date | string | null,
+ visitation_location: string,
+ visitation_address: string,
+ visitation_description: string,
+ visitation_end_time: Date | string | null,
+ funeral_date: Date | string | null,
+ funeral_end_time: 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
+ Images: Image[],
+ Family: Partial,
+ familyCuid: string,
+ status: string,
+ donation_status: string,
+ duration: string,
+ start_date: Date | string | null
+ goal_met_date: Date | string | null
+ PageDonations: PageDonation[]
+ last_donation_date: Date | string | null
+ Reply: Reply[]
+ }
+
+export type Family = {
+ cuid: string;
+ family_name: string;
+ stripe_account_id: string | null;
+ created_at: Date | string | null;
+ updated_at: Date | string | null;
+ advocateCuid: string;
+ Pages: Page[];
+ FamilyMembers: User[];
+ AdvocateResponsible: User;
+ FamilyDonations: PageDonation[];
+ FamilyDonationPayouts: DonationPayout[];
+}
+
+export type User = {
+ Family: Partial;
+ isActive: boolean,
+ cuid: string,
+ first_name: string,
+ last_name: string,
+ user_role: string,
+ email: string,
+ middle_name: string,
+ phone: string,
+ Pages: Page[],
+ familyCuid: string,
+ AdvocateFamily: User[]
+ //PageDonations: PageDonation[]
+ //DonationPayouts: DonationPayout[]
+}
+
+export type PageDonation = {
+ cuid: string,
+ userCuid: string,
+ familyCuid: String,
+ pageCuid: string,
+ success: boolean,
+ transaction_id: string,
+ amount: number,
+ donorFirstName: string,
+ donorLastName: string,
+ donorEmail: string,
+ isAnonymous: boolean,
+ comments: string,
+ Page: Page,
+ donationDate: Date | string | null
+ //User: User
+}
+
+
+export type donation_payout = {
+ cuid: string,
+ transaction_id: string,
+ userCuid: string
+ amount_to_record: number,
+ transaction_recording_date: Date | string | null,
+ familyCuid: string,
+ page: Page
+}
+
+export type Image = {
+ cuid: string
+ url: string
+ pageCuid: string
+ //Page: Page
+}
+
+export type Reply = {
+ cuid: string,
+ pageCuid: string
+ familyCuid: string
+ name: string
+ reply: string
+ date: Date | string | null | undefined
+ suspended: boolean
+}