diff --git a/src/backend/src/controllers/organizations.controllers.ts b/src/backend/src/controllers/organizations.controllers.ts index 966691d9bb..6ed04b5580 100644 --- a/src/backend/src/controllers/organizations.controllers.ts +++ b/src/backend/src/controllers/organizations.controllers.ts @@ -22,28 +22,6 @@ export default class OrganizationsController { } } - static async setImages(req: Request, res: Response, next: NextFunction) { - try { - const { applyInterestImage = [], exploreAsGuestImage = [] } = req.files as { - applyInterestImage?: Express.Multer.File[]; - exploreAsGuestImage?: Express.Multer.File[]; - }; - - const applyInterestFile = applyInterestImage[0] || null; - const exploreAsGuestFile = exploreAsGuestImage[0] || null; - - const newImages = await OrganizationsService.setImages( - applyInterestFile, - exploreAsGuestFile, - req.currentUser, - req.organization - ); - - res.status(200).json(newImages); - } catch (error: unknown) { - next(error); - } - } static async getAllUsefulLinks(req: Request, res: Response, next: NextFunction) { try { const links = await OrganizationsService.getAllUsefulLinks(req.organization.organizationId); @@ -97,15 +75,6 @@ export default class OrganizationsController { } } - static async getOrganizationImages(req: Request, res: Response, next: NextFunction) { - try { - const images = await OrganizationsService.getOrganizationImages(req.organization.organizationId); - res.status(200).json(images); - } catch (error: unknown) { - next(error); - } - } - static async setOrganizationFeaturedProjects(req: Request, res: Response, next: NextFunction) { try { const { projectIds } = req.body; @@ -142,6 +111,29 @@ export default class OrganizationsController { } } + static async setPlatformLogoImage(req: Request, res: Response, next: NextFunction) { + try { + if (!req.file) { + throw new HttpException(400, 'Invalid or undefined image data'); + } + + const updatedOrg = await OrganizationsService.setPlatformLogoImage(req.file, req.currentUser, req.organization); + + res.status(200).json(updatedOrg); + } catch (error: unknown) { + next(error); + } + } + + static async getPlatformLogoImage(req: Request, res: Response, next: NextFunction) { + try { + const platformLogoImageId = await OrganizationsService.getPlatformLogoImage(req.organization.organizationId); + res.status(200).json(platformLogoImageId); + } catch (error: unknown) { + next(error); + } + } + static async setNewMemberImage(req: Request, res: Response, next: NextFunction) { try { if (!req.file) { @@ -181,6 +173,20 @@ export default class OrganizationsController { } } + static async setPlatformDescription(req: Request, res: Response, next: NextFunction) { + try { + const updatedOrg = await OrganizationsService.setPlatformDescription( + req.body.platformDescription, + req.currentUser, + req.organization + ); + + res.status(200).json(updatedOrg); + } catch (error: unknown) { + next(error); + } + } + static async getOrganizationFeaturedProjects(req: Request, res: Response, next: NextFunction) { try { const featuredProjects = await OrganizationsService.getOrganizationFeaturedProjects(req.organization.organizationId); diff --git a/src/backend/src/prisma/migrations/20260221213604_guest_home_page/migration.sql b/src/backend/src/prisma/migrations/20260221213604_guest_home_page/migration.sql new file mode 100644 index 0000000000..efb65c70d9 --- /dev/null +++ b/src/backend/src/prisma/migrations/20260221213604_guest_home_page/migration.sql @@ -0,0 +1,12 @@ +/* + Warnings: + + - You are about to drop the column `applyInterestImageId` on the `Organization` table. All the data in the column will be lost. + - You are about to drop the column `exploreAsGuestImageId` on the `Organization` table. All the data in the column will be lost. + +*/ +-- AlterTable +ALTER TABLE "Organization" DROP COLUMN "applyInterestImageId", +DROP COLUMN "exploreAsGuestImageId", +ADD COLUMN "platformDescription" TEXT NOT NULL DEFAULT '', +ADD COLUMN "platformLogoImageId" TEXT; diff --git a/src/backend/src/prisma/schema.prisma b/src/backend/src/prisma/schema.prisma index 3d3ba18d35..a00a8ccbbc 100644 --- a/src/backend/src/prisma/schema.prisma +++ b/src/backend/src/prisma/schema.prisma @@ -1027,12 +1027,12 @@ enum DayOfWeek { } model Schedule_Slot { - scheduleSlotId String @id @default(uuid()) - startTime DateTime - endTime DateTime - allDay Boolean @default(false) - eventId String - event Event @relation(fields: [eventId], references: [eventId]) + scheduleSlotId String @id @default(uuid()) + startTime DateTime + endTime DateTime + allDay Boolean @default(false) + eventId String + event Event @relation(fields: [eventId], references: [eventId]) @@index([endTime]) @@index([startTime]) @@ -1088,7 +1088,7 @@ model Event { shops Shop[] machinery Machinery[] workPackages Work_Package[] - documents Document[] + documents Document[] status Event_Status initialDateScheduled DateTime? questionDocumentLink String? @@ -1312,8 +1312,6 @@ model Organization { advisor User? @relation(name: "advisor", fields: [advisorId], references: [userId]) advisorId String? description String @default("") - applyInterestImageId String? - exploreAsGuestImageId String? newMemberImageId String? logoImageId String? slackWorkspaceId String? @@ -1322,6 +1320,8 @@ model Organization { partReviewSampleImageId String? partReviewGuideLink String? sponsorshipNotificationsSlackChannelId String? + platformDescription String @default("") + platformLogoImageId String? // Relation references wbsElements WBS_Element[] diff --git a/src/backend/src/prisma/seed-data/users.seed.ts b/src/backend/src/prisma/seed-data/users.seed.ts index dbb31d55ed..5c7e31c84d 100644 --- a/src/backend/src/prisma/seed-data/users.seed.ts +++ b/src/backend/src/prisma/seed-data/users.seed.ts @@ -62,6 +62,20 @@ const joeBlow: Prisma.UserCreateInput = { } }; +const guestUser: Prisma.UserCreateInput = { + firstName: 'Guest', + lastName: 'User', + googleAuthId: 'guest-google-id', + email: 'guest@husky.neu.edu', + emailId: 'guest', + userSettings: { + create: { + defaultTheme: Theme.DARK, + slackId: SLACK_ID ? SLACK_ID : 'guest' + } + } +}; + const wonderwoman: Prisma.UserCreateInput = { firstName: 'Diana', lastName: 'Prince', @@ -994,6 +1008,7 @@ export const dbSeedAllUsers = { thomasEmrax, joeShmoe, joeBlow, + guestUser, wonderwoman, flash, aquaman, diff --git a/src/backend/src/prisma/seed.ts b/src/backend/src/prisma/seed.ts index a785823598..f317b62b20 100644 --- a/src/backend/src/prisma/seed.ts +++ b/src/backend/src/prisma/seed.ts @@ -125,10 +125,11 @@ const performSeed: () => Promise = async () => { userCreatedId: thomasEmrax.userId, description: 'Northeastern Electric Racing is a student-run organization at Northeastern University building all-electric formula-style race cars from scratch to compete in Forumla Hybrid + Electric Formula SAE (FSAE).', - applyInterestImageId: '1_iak6ord4JP9TcR1sOYopyEs6EjTKQpw', - exploreAsGuestImageId: '1wRes7V_bMm9W7_3JCIDXYkMUiy6B3wRI', applicationLink: - 'https://docs.google.com/forms/d/e/1FAIpQLSeCvG7GqmZm_gmSZiahbVTW9ZFpEWG0YfGQbkSB_whhHzxXpA/closedform' + 'https://docs.google.com/forms/d/e/1FAIpQLSeCvG7GqmZm_gmSZiahbVTW9ZFpEWG0YfGQbkSB_whhHzxXpA/closedform', + platformDescription: + 'Finishline is a Project Management Dashboard developed by the Software Team at Northeastern Electric Racing.', + platformLogoImageId: '1auQO3GYydZOo1-vCn0D2iyCfaxaVFssx' } }); @@ -264,6 +265,7 @@ const performSeed: () => Promise = async () => { const regina = await createUser(dbSeedAllUsers.regina, RoleEnum.MEMBER, organizationId); const patrick = await createUser(dbSeedAllUsers.patrick, RoleEnum.MEMBER, organizationId); const spongebob = await createUser(dbSeedAllUsers.spongebob, RoleEnum.MEMBER, organizationId); + await createUser(dbSeedAllUsers.guestUser, RoleEnum.GUEST, organizationId); await UsersService.updateUserRole(cyborg.userId, thomasEmrax, 'APP_ADMIN', ner); @@ -367,21 +369,15 @@ const performSeed: () => Promise = async () => { const mechanical = await TeamsService.createTeamType( batman, 'Mechanical', - 'YouTubeIcon', + 'Construction', 'This is the mechanical team', ner ); - const software = await TeamsService.createTeamType( - thomasEmrax, - 'Software', - 'InstagramIcon', - 'This is the software team', - ner - ); + const software = await TeamsService.createTeamType(thomasEmrax, 'Software', 'Code', 'This is the software team', ner); const electrical = await TeamsService.createTeamType( cyborg, 'Electrical', - 'SettingsIcon', + 'ElectricBolt', 'This is the electrical team', ner ); diff --git a/src/backend/src/routes/organizations.routes.ts b/src/backend/src/routes/organizations.routes.ts index ebc6d9f869..bdb50e59a9 100644 --- a/src/backend/src/routes/organizations.routes.ts +++ b/src/backend/src/routes/organizations.routes.ts @@ -11,14 +11,6 @@ const upload = multer({ limits: { fileSize: MAX_FILE_SIZE }, storage: memoryStor organizationRouter.get('/current', OrganizationsController.getCurrentOrganization); organizationRouter.post('/useful-links/set', ...linkValidators, validateInputs, OrganizationsController.setUsefulLinks); organizationRouter.get('/useful-links', OrganizationsController.getAllUsefulLinks); -organizationRouter.post( - '/images/update', - upload.fields([ - { name: 'applyInterestImage', maxCount: 1 }, - { name: 'exploreAsGuestImage', maxCount: 1 } - ]), - OrganizationsController.setImages -); organizationRouter.post( '/application-link/update', @@ -51,6 +43,14 @@ organizationRouter.post( ); organizationRouter.post('/logo/update', upload.single('logo'), OrganizationsController.setLogoImage); organizationRouter.get('/logo', OrganizationsController.getOrganizationLogoImage); + +organizationRouter.post( + '/platform-logo/update', + upload.single('platformLogo'), + OrganizationsController.setPlatformLogoImage +); +organizationRouter.get('/platform-logo', OrganizationsController.getPlatformLogoImage); + organizationRouter.post( '/new-member-image/update', upload.single('newMemberImage'), @@ -63,6 +63,12 @@ organizationRouter.post( validateInputs, OrganizationsController.setOrganizationDescription ); +organizationRouter.post( + '/platform-description/set', + body('platformDescription').isString(), + validateInputs, + OrganizationsController.setPlatformDescription +); organizationRouter.get('/featured-projects', OrganizationsController.getOrganizationFeaturedProjects); organizationRouter.post( '/workspaceId/set', diff --git a/src/backend/src/services/organizations.services.ts b/src/backend/src/services/organizations.services.ts index 1a6620b88a..719a5adf76 100644 --- a/src/backend/src/services/organizations.services.ts +++ b/src/backend/src/services/organizations.services.ts @@ -104,37 +104,6 @@ export default class OrganizationsService { return newLinks; } - /** - * sets an organizations images - * @param submitter the user who is setting the images - * @param organizationId the organization which the images will be set up - * @param images the images which are being set - */ - static async setImages( - applyInterestImage: Express.Multer.File | null, - exploreAsGuestImage: Express.Multer.File | null, - submitter: User, - organization: Organization - ) { - if (!(await userHasPermission(submitter.userId, organization.organizationId, isAdmin))) { - throw new AccessDeniedAdminOnlyException('update images'); - } - - const applyInterestImageData = applyInterestImage ? await uploadFile(applyInterestImage) : null; - const exploreAsGuestImageData = exploreAsGuestImage ? await uploadFile(exploreAsGuestImage) : null; - const updateData = { - ...(applyInterestImageData && { applyInterestImageId: applyInterestImageData.id }), - ...(exploreAsGuestImageData && { exploreAsGuestImageId: exploreAsGuestImageData.id }) - }; - - const newImages = await prisma.organization.update({ - where: { organizationId: organization.organizationId }, - data: updateData - }); - - return newImages; - } - /** Gets all the useful links for an organization @param organizationId the organization to get the links for @@ -255,26 +224,6 @@ export default class OrganizationsService { return updatedOrganization; } - /** - * Gets all organization Images for the given organization Id - * @param organizationId organization Id of the milestone - * @returns all the milestones from the given organization - */ - static async getOrganizationImages(organizationId: string) { - const organization = await prisma.organization.findUnique({ - where: { organizationId } - }); - - if (!organization) { - throw new NotFoundException('Organization', organizationId); - } - - return { - applyInterestImage: organization.applyInterestImageId, - exploreAsGuestImage: organization.exploreAsGuestImageId - }; - } - /** * Updates the featured projects of an organization * @param projectIds project ids of featured projects @@ -429,6 +378,23 @@ export default class OrganizationsService { return updatedOrg; } + /** + * Sets the platform description of a given organization. + * @param platformDescription the new platform description + * @param submitter the user making the change + * @param organization the organization whose platform description is changing + * @throws if the user is not an admin + */ + static async setPlatformDescription(platformDescription: string, submitter: User, organization: Organization) { + if (!(await userHasPermission(submitter.userId, organization.organizationId, isAdmin))) { + throw new AccessDeniedAdminOnlyException('set platform description'); + } + return prisma.organization.update({ + where: { organizationId: organization.organizationId }, + data: { platformDescription } + }); + } + /** * Gets the featured projects for the given organization Id * @param organizationId the organization to get the projects for @@ -596,4 +562,50 @@ export default class OrganizationsService { return updatedOrg.financeDelegates.map(userTransformer); } + + /** + * sets an organizations platform image + * @param submitter the user who is setting the images + * @param organizationId the organization which the images will be set up + * @param images the images which are being set + */ + static async setPlatformLogoImage( + platformLogoImageId: Express.Multer.File | null, + submitter: User, + organization: Organization + ) { + if (!(await userHasPermission(submitter.userId, organization.organizationId, isAdmin))) { + throw new AccessDeniedAdminOnlyException('update platform logo'); + } + + const platformLogoImageData = platformLogoImageId ? await uploadFile(platformLogoImageId) : null; + + const updateData = { + ...(platformLogoImageData && { platformLogoImageId: platformLogoImageData.id }) + }; + + const newImages = await prisma.organization.update({ + where: { organizationId: organization.organizationId }, + data: updateData + }); + + return newImages; + } + + /** + * Gets platform logo image for the given organization + * @param organizationId organization Id of the milestone + * @returns all the milestones from the given organization + */ + static async getPlatformLogoImage(organizationId: string) { + const organization = await prisma.organization.findUnique({ + where: { organizationId } + }); + + if (!organization) { + throw new NotFoundException('Organization', organizationId); + } + + return organization.platformLogoImageId; + } } diff --git a/src/backend/src/transformers/organizationTransformer.ts b/src/backend/src/transformers/organizationTransformer.ts index 89ddc1981c..a551216a54 100644 --- a/src/backend/src/transformers/organizationTransformer.ts +++ b/src/backend/src/transformers/organizationTransformer.ts @@ -5,8 +5,8 @@ export const organizationTransformer = (organization: Organization): Organizatio return { ...organization, applicationLink: organization.applicationLink ?? undefined, - applyInterestImageId: organization.applyInterestImageId ?? undefined, - exploreAsGuestImageId: organization.exploreAsGuestImageId ?? undefined, - newMemberImageId: organization.newMemberImageId ?? undefined + newMemberImageId: organization.newMemberImageId ?? undefined, + platformDescription: organization.platformDescription ?? '', + platformLogoImageId: organization.platformLogoImageId ?? undefined }; }; diff --git a/src/backend/tests/unit/organization.test.ts b/src/backend/tests/unit/organization.test.ts index 3bdec303a0..b42f24594a 100644 --- a/src/backend/tests/unit/organization.test.ts +++ b/src/backend/tests/unit/organization.test.ts @@ -42,46 +42,6 @@ describe('Organization Tests', () => { }); }); - describe('Set Images', () => { - const file1 = { originalname: 'image1.png' } as Express.Multer.File; - const file2 = { originalname: 'image2.png' } as Express.Multer.File; - const file3 = { originalname: 'image3.png' } as Express.Multer.File; - it('Fails if user is not an admin', async () => { - await expect( - OrganizationsService.setImages(file1, file2, await createTestUser(wonderwomanGuest, orgId), organization) - ).rejects.toThrow(new AccessDeniedAdminOnlyException('update images')); - }); - - it('Succeeds and updates all the images', async () => { - const testBatman = await createTestUser(batmanAppAdmin, orgId); - (uploadFile as Mock).mockImplementation((file) => { - return Promise.resolve({ name: `${file.originalname}`, id: `uploaded-${file.originalname}` }); - }); - - await OrganizationsService.setImages(file1, file2, testBatman, organization); - - const oldOrganization = await prisma.organization.findUnique({ - where: { - organizationId: orgId - } - }); - - expect(oldOrganization).not.toBeNull(); - expect(oldOrganization?.applyInterestImageId).toBe('uploaded-image1.png'); - expect(oldOrganization?.exploreAsGuestImageId).toBe('uploaded-image2.png'); - - await OrganizationsService.setImages(file1, file3, testBatman, organization); - - const updatedOrganization = await prisma.organization.findUnique({ - where: { - organizationId: orgId - } - }); - - expect(updatedOrganization?.exploreAsGuestImageId).toBe('uploaded-image3.png'); - }); - }); - describe('Set Useful Links', () => { it('Fails if user is not an admin', async () => { await expect( @@ -204,30 +164,6 @@ describe('Organization Tests', () => { }); }); - describe('Get Organization Images', () => { - it('Fails if an organization does not exist', async () => { - await expect(async () => await OrganizationsService.getOrganizationImages('1')).rejects.toThrow( - new NotFoundException('Organization', '1') - ); - }); - - it('Succeeds and gets all the images', async () => { - const testBatman = await createTestUser(batmanAppAdmin, orgId); - await createTestLinkType(testBatman, orgId); - await OrganizationsService.setImages( - { originalname: 'image1.png' } as Express.Multer.File, - { originalname: 'image2.png' } as Express.Multer.File, - testBatman, - organization - ); - const images = await OrganizationsService.getOrganizationImages(orgId); - - expect(images).not.toBeNull(); - expect(images.applyInterestImage).toBe('uploaded-image1.png'); - expect(images.exploreAsGuestImage).toBe('uploaded-image2.png'); - }); - }); - describe('Set Logo', () => { const file1 = { originalname: 'image1.png' } as Express.Multer.File; const file2 = { originalname: 'image2.png' } as Express.Multer.File; @@ -359,4 +295,64 @@ describe('Organization Tests', () => { expect(updatedOrganization.partReviewGuideLink).toBe('newlink'); }); }); + + describe('Set Organization Platform Logo', () => { + const file1 = { originalname: 'image1.png' } as Express.Multer.File; + const file2 = { originalname: 'image2.png' } as Express.Multer.File; + const file3 = { originalname: 'image3.png' } as Express.Multer.File; + it('Fails if user is not an admin', async () => { + await expect( + OrganizationsService.setPlatformLogoImage(file1, await createTestUser(wonderwomanGuest, orgId), organization) + ).rejects.toThrow(new AccessDeniedAdminOnlyException('update platform logo')); + }); + + it('Succeeds and updates all the images', async () => { + const testBatman = await createTestUser(batmanAppAdmin, orgId); + (uploadFile as Mock).mockImplementation((file) => { + return Promise.resolve({ name: `${file.originalname}`, id: `uploaded-${file.originalname}` }); + }); + + await OrganizationsService.setPlatformLogoImage(file2, testBatman, organization); + + const oldOrganization = await prisma.organization.findUnique({ + where: { + organizationId: orgId + } + }); + + expect(oldOrganization).not.toBeNull(); + expect(oldOrganization?.platformLogoImageId).toBe('uploaded-image2.png'); + + await OrganizationsService.setPlatformLogoImage(file3, testBatman, organization); + + const updatedOrganization = await prisma.organization.findUnique({ + where: { + organizationId: orgId + } + }); + + expect(updatedOrganization?.platformLogoImageId).toBe('uploaded-image3.png'); + }); + }); + + describe('Get Organization Platform Logo', () => { + it('Fails if an organization does not exist', async () => { + await expect(async () => await OrganizationsService.getPlatformLogoImage('1')).rejects.toThrow( + new NotFoundException('Organization', '1') + ); + }); + + it('Succeeds and gets the image', async () => { + const testBatman = await createTestUser(batmanAppAdmin, orgId); + await OrganizationsService.setPlatformLogoImage( + { originalname: 'image1.png' } as Express.Multer.File, + testBatman, + organization + ); + const image = await OrganizationsService.getPlatformLogoImage(orgId); + + expect(image).not.toBeNull(); + expect(image).toBe('uploaded-image1.png'); + }); + }); }); diff --git a/src/backend/tests/unmocked/organization.test.ts b/src/backend/tests/unmocked/organization.test.ts index 22598ca555..4712a97137 100644 --- a/src/backend/tests/unmocked/organization.test.ts +++ b/src/backend/tests/unmocked/organization.test.ts @@ -4,8 +4,7 @@ import { batmanAppAdmin, flashAdmin, supermanAdmin, wonderwomanGuest } from '../ import { createTestLinkType, createTestOrganization, createTestUser, resetUsers } from '../test-utils.js'; import prisma from '../../src/prisma/prisma.js'; import { testLink1 } from '../test-data/organizations.test-data.js'; -import { uploadFile } from '../../src/utils/google-integration.utils.js'; -import { Mock, vi } from 'vitest'; +import { vi } from 'vitest'; import OrganizationsService from '../../src/services/organizations.services.js'; import { Organization } from '@prisma/client'; @@ -42,46 +41,6 @@ describe('Organization Tests', () => { }); }); - describe('Set Images', () => { - const file1 = { originalname: 'image1.png' } as Express.Multer.File; - const file2 = { originalname: 'image2.png' } as Express.Multer.File; - const file3 = { originalname: 'image3.png' } as Express.Multer.File; - it('Fails if user is not an admin', async () => { - await expect( - OrganizationsService.setImages(file1, file2, await createTestUser(wonderwomanGuest, orgId), organization) - ).rejects.toThrow(new AccessDeniedAdminOnlyException('update images')); - }); - - it('Succeeds and updates all the images', async () => { - const testBatman = await createTestUser(batmanAppAdmin, orgId); - (uploadFile as Mock).mockImplementation((file) => { - return Promise.resolve({ id: `uploaded-${file.originalname}` }); - }); - - await OrganizationsService.setImages(file1, file2, testBatman, organization); - - const oldOrganization = await prisma.organization.findUnique({ - where: { - organizationId: orgId - } - }); - - expect(oldOrganization).not.toBeNull(); - expect(oldOrganization?.applyInterestImageId).toBe('uploaded-image1.png'); - expect(oldOrganization?.exploreAsGuestImageId).toBe('uploaded-image2.png'); - - await OrganizationsService.setImages(file1, file3, testBatman, organization); - - const updatedOrganization = await prisma.organization.findUnique({ - where: { - organizationId: orgId - } - }); - - expect(updatedOrganization?.exploreAsGuestImageId).toBe('uploaded-image3.png'); - }); - }); - describe('Set Useful Links', () => { it('Fails if user is not an admin', async () => { await expect( diff --git a/src/frontend/src/apis/organizations.api.ts b/src/frontend/src/apis/organizations.api.ts index 6ba806add1..6cfdcd07b2 100644 --- a/src/frontend/src/apis/organizations.api.ts +++ b/src/frontend/src/apis/organizations.api.ts @@ -42,6 +42,12 @@ export const setOrganizationDescription = async (description: string) => { }); }; +export const setPlatformDescription = async (platformDescription: string) => { + return axios.post(apiUrls.organizationsSetPlatformDescription(), { + platformDescription + }); +}; + export const getOrganizationLogo = async () => { return axios.get(apiUrls.organizationsLogoImage(), { transformResponse: (data) => JSON.parse(data) @@ -66,6 +72,18 @@ export const getOrganizationNewMemberImage = async () => { }); }; +export const setOrganizationPlatformLogoImage = async (file: File) => { + const formData = new FormData(); + formData.append('platformLogo', file); + return axios.post(apiUrls.organizationsSetPlatformLogoImage(), formData); +}; + +export const getOrganizationPlatformLogoImage = async () => { + return axios.get(apiUrls.organizationsPlatformLogoImage(), { + transformResponse: (data) => JSON.parse(data) + }); +}; + export const setOrganizationFeaturedProjects = async (featuredProjectIds: string[]) => { return axios.post(apiUrls.organizationsSetFeaturedProjects(), { projectIds: featuredProjectIds @@ -93,15 +111,6 @@ export const downloadGoogleImage = async (fileId: string): Promise => { return imageBlob; }; -export const setOrganizationImages = (images: File[]) => { - const formData = new FormData(); - - formData.append('applyInterestImage', images[0]); - formData.append('exploreAsGuestImage', images[1]); - - return axios.post<{ message: string }>(apiUrls.organizationsSetImages(), formData, {}); -}; - /** * Sets the contacts for an organization * @param contacts all the contact information that is being set diff --git a/src/frontend/src/app/AppAuthenticated.tsx b/src/frontend/src/app/AppAuthenticated.tsx index 8ab368c60c..0094ddba8c 100644 --- a/src/frontend/src/app/AppAuthenticated.tsx +++ b/src/frontend/src/app/AppAuthenticated.tsx @@ -70,7 +70,7 @@ const AppAuthenticated: React.FC = ({ userId, userRole }) return userSettingsData.slackId || isGuest(userRole) ? ( - {!onGuestHomePage && ( + { <> { @@ -108,12 +108,12 @@ const AppAuthenticated: React.FC = ({ userId, userRole }) setMoveContent={setMoveContent} /> - )} + } - + diff --git a/src/frontend/src/hooks/organizations.hooks.ts b/src/frontend/src/hooks/organizations.hooks.ts index cff6cd0f45..f9eba94a7b 100644 --- a/src/frontend/src/hooks/organizations.hooks.ts +++ b/src/frontend/src/hooks/organizations.hooks.ts @@ -6,6 +6,7 @@ import { getFeaturedProjects, getCurrentOrganization, setOrganizationDescription, + setPlatformDescription, setOrganizationFeaturedProjects, setOrganizationWorkspaceId, setOrganizationLogo, @@ -13,14 +14,15 @@ import { updateApplicationLink, setOnboardingText, updateOrganizationContacts, - setOrganizationImages, getPartReviewGuideLink, setPartReviewGuideLink, setSlackSponsorshipNotificationSlackChannelId, getFinanceDelegates, setFinanceDelegates, setOrganizationNewMemberImage, - getOrganizationNewMemberImage + getOrganizationNewMemberImage, + setOrganizationPlatformLogoImage, + getOrganizationPlatformLogoImage } from '../apis/organizations.api'; import { downloadGoogleImage } from '../apis/organizations.api'; @@ -66,22 +68,6 @@ export const useProvideOrganization = (): OrganizationProvider => { }; }; -export const useSetOrganizationImages = () => { - const queryClient = useQueryClient(); - - return useMutation( - async (images: File[]) => { - const { data } = await setOrganizationImages(images); - return data; - }, - { - onSuccess: () => { - queryClient.invalidateQueries(['organizations']); - } - } - ); -}; - export const useFeaturedProjects = () => { return useQuery(['organizations', 'featured-projects'], async () => { const { data } = await getFeaturedProjects(); @@ -164,6 +150,22 @@ export const useSetOrganizationDescription = () => { ); }; +export const useSetPlatformDescription = () => { + const queryClient = useQueryClient(); + return useMutation( + ['organizations', 'platform-description'], + async (platformDescription: string) => { + const { data } = await setPlatformDescription(platformDescription); + return data; + }, + { + onSuccess: () => { + queryClient.invalidateQueries(['organizations']); + } + } + ); +}; + export const useSetFeaturedProjects = () => { const queryClient = useQueryClient(); return useMutation( @@ -235,6 +237,25 @@ export const useSetOrganizationNewMemberImage = () => { }); }; +export const useSetOrganizationPlatformLogoImage = () => { + const queryClient = useQueryClient(); + return useMutation(['organizations', 'platform-logo'], async (file: File) => { + const { data } = await setOrganizationPlatformLogoImage(file); + queryClient.invalidateQueries(['organizations']); + return data; + }); +}; + +export const useOrganizationPlatformLogoImage = () => { + return useQuery(['organizations', 'platform-logo'], async () => { + const { data: fileId } = await getOrganizationPlatformLogoImage(); + if (!fileId) { + return; + } + return await downloadGoogleImage(fileId); + }); +}; + /* * Custom React Hook to fetch confluence guide for current * organization in backend diff --git a/src/frontend/src/layouts/Sidebar/Sidebar.tsx b/src/frontend/src/layouts/Sidebar/Sidebar.tsx index 6fd7b11f4d..93e731e345 100644 --- a/src/frontend/src/layouts/Sidebar/Sidebar.tsx +++ b/src/frontend/src/layouts/Sidebar/Sidebar.tsx @@ -9,6 +9,10 @@ import styles from '../../stylesheets/layouts/sidebar/sidebar.module.css'; import { Typography, Box, IconButton, Divider } from '@mui/material'; import HomeIcon from '@mui/icons-material/Home'; import AlignHorizontalLeftIcon from '@mui/icons-material/AlignHorizontalLeft'; +import RateReviewIcon from '@mui/icons-material/RateReview'; +import DashboardIcon from '@mui/icons-material/Dashboard'; +// To be uncommented after guest sponsors page is developed +// import VolunteerActivismIcon from '@mui/icons-material/VolunteerActivism'; import FolderIcon from '@mui/icons-material/Folder'; import SyncAltIcon from '@mui/icons-material/SyncAlt'; import GroupIcon from '@mui/icons-material/Group'; @@ -21,7 +25,12 @@ import NavUserMenu from '../PageTitle/NavUserMenu'; import DrawerHeader from '../../components/DrawerHeader'; import { Cached, ChevronLeft, ChevronRight } from '@mui/icons-material'; import { useHomePageContext } from '../../app/HomePageContext'; +// once divisions developed, import TeamType from shared import { isGuest } from 'shared'; +// To be uncommented after divisions page is developed +// import * as MuiIcons from '@mui/icons-material'; +// import { useAllTeamTypes } from '../../hooks/team-types.hooks'; +// import ErrorPage from '../../pages/ErrorPage'; import BarChartIcon from '@mui/icons-material/BarChart'; import { useCurrentUser } from '../../hooks/users.hooks'; import QueryStatsIcon from '@mui/icons-material/QueryStats'; @@ -40,29 +49,70 @@ const Sidebar = ({ drawerOpen, setDrawerOpen, moveContent, setMoveContent }: Sid const [openSubmenu, setOpenSubmenu] = useState(null); const { onPNMHomePage, onOnboardingHomePage } = useHomePageContext(); const user = useCurrentUser(); + const { onGuestHomePage } = useHomePageContext(); + // const { isError: teamsError, error: teamsErrorMsg, data: teams } = useAllTeamTypes(); + // To be uncommented once guest divisions pages are developed + // const allTeams: LinkItem[] = (teams ?? []).map((team: TeamType) => { + // const IconComponent = MuiIcons[(team.iconName in MuiIcons ? team.iconName : 'Circle') as keyof typeof MuiIcons]; + // return { + // name: team.name, + // icon: , + // route: routes.TEAMS + '/' + team.teamTypeId + // }; + // }); + + // if (teamsError) return ; const memberLinkItems: LinkItem[] = [ { name: 'Home', icon: , - route: routes.HOME + route: onGuestHomePage ? routes.HOME_GUEST : routes.HOME }, - { + !onGuestHomePage && { name: 'Gantt', icon: , route: routes.GANTT }, - { - name: 'Projects', - icon: , - route: routes.PROJECTS - }, - { + !onGuestHomePage + ? { + name: 'Projects', + icon: , + route: routes.PROJECTS + } + : { + name: 'Project Management', + icon: , + route: routes.PROJECTS, + subItems: [ + { + name: 'Gantt', + icon: , + route: routes.GANTT + }, + { + name: 'Projects', + icon: , + route: routes.PROJECTS + }, + { + name: 'Change Requests', + icon: , + route: routes.CHANGE_REQUESTS + }, + { + name: 'Design Review', + icon: , + route: routes.CALENDAR + } + ] + }, + !onGuestHomePage && { name: 'Change Requests', icon: , route: routes.CHANGE_REQUESTS }, - { + !onGuestHomePage && { name: 'Finance', icon: , route: routes.FINANCE, @@ -84,29 +134,49 @@ const Sidebar = ({ drawerOpen, setDrawerOpen, moveContent, setMoveContent }: Sid } ] }, - { + + // Teams tab here to be replaced with below code once guest divisions is developed + !onGuestHomePage && { name: 'Teams', icon: , route: routes.TEAMS }, - { + // !onGuestHomePage + // ? { + // name: 'Teams', + // icon: , + // route: routes.TEAMS + // } + // : { + // name: 'Divisions', + // icon: , + // route: routes.TEAMS, + // subItems: allTeams + // }, + !onGuestHomePage && { name: 'Calendar', icon: , route: routes.CALENDAR }, - { + !onGuestHomePage && { name: 'Retrospective', icon: , route: routes.RETROSPECTIVE }, + // To be uncommented once guest mode sponsors page is developed + // onGuestHomePage && { + // name: 'Sponsors', + // icon: , + // route: routes.RETROSPECTIVE + // }, { name: 'Info', icon: , route: routes.INFO } - ]; + ].filter(Boolean) as LinkItem[]; - if (!isGuest(user.role)) { + if (!isGuest(user.role) && !onGuestHomePage) { memberLinkItems.splice(6, 0, { name: 'Statistics', icon: , diff --git a/src/frontend/src/pages/AdminToolsPage/RecruitmentConfig/AdminToolsRecruitmentConfig.tsx b/src/frontend/src/pages/AdminToolsPage/RecruitmentConfig/AdminToolsRecruitmentConfig.tsx index 40cfebe5e4..9a815ba027 100644 --- a/src/frontend/src/pages/AdminToolsPage/RecruitmentConfig/AdminToolsRecruitmentConfig.tsx +++ b/src/frontend/src/pages/AdminToolsPage/RecruitmentConfig/AdminToolsRecruitmentConfig.tsx @@ -1,26 +1,30 @@ -import { Box, Grid, Typography } from '@mui/material'; +import { Box, FormControl, Grid, Typography } from '@mui/material'; import MilestoneTable from './MilestoneTable'; import FAQsTable from './FAQTable'; -import { useToast } from '../../../hooks/toasts.hooks'; -import NERUploadButton from '../../../components/NERUploadButton'; import React, { useState } from 'react'; -import { useCurrentOrganization, useSetOrganizationImages } from '../../../hooks/organizations.hooks'; +import { useForm } from 'react-hook-form'; +import { yupResolver } from '@hookform/resolvers/yup'; +import * as yup from 'yup'; +import { + useCurrentOrganization, + useSetOrganizationPlatformLogoImage, + useSetPlatformDescription +} from '../../../hooks/organizations.hooks'; import LoadingIndicator from '../../../components/LoadingIndicator'; -import { useGetImageUrl } from '../../../hooks/onboarding.hook'; import ErrorPage from '../../ErrorPage'; import ApplicationLinkTable from './ApplicationLinkTable'; +import { useGetImageUrl } from '../../../hooks/onboarding.hook'; +import NERUploadButton from '../../../components/NERUploadButton'; +import NERSuccessButton from '../../../components/NERSuccessButton'; +import ReactHookTextField from '../../../components/ReactHookTextField'; +import { useToast } from '../../../hooks/toasts.hooks'; import { MAX_FILE_SIZE } from 'shared'; -const AdminToolsRecruitmentConfig: React.FC = () => { - const { - mutateAsync: organizationImages, - isLoading: organizationImagesIsLoading, - isError: organizationImagesIsError, - error: organizationImagesError - } = useSetOrganizationImages(); - - const toast = useToast(); +const platformDescriptionSchema = yup.object().shape({ + platformDescription: yup.string().required() +}); +const AdminToolsRecruitmentConfig: React.FC = () => { const { data: organization, isLoading: organizationIsLoading, @@ -28,49 +32,55 @@ const AdminToolsRecruitmentConfig: React.FC = () => { error: organizationError } = useCurrentOrganization(); - const { data: applyInterestImageUrl } = useGetImageUrl(organization?.applyInterestImageId ?? null); - const { data: exploreGuestImageUrl } = useGetImageUrl(organization?.exploreAsGuestImageId ?? null); + const { mutateAsync: setPlatformLogoImage, isLoading: platformLogoLoading } = useSetOrganizationPlatformLogoImage(); + const { mutateAsync: setPlatformDescriptionMutation, isLoading: platformDescriptionSaving } = useSetPlatformDescription(); - const [addedImage1, setAddedImage1] = useState(undefined); - const [addedImage2, setAddedImage2] = useState(undefined); - const [isUploadingApply, setIsUploadingApply] = useState(false); - const [isUploadingExplore, setIsUploadingExplore] = useState(false); + const { data: platformLogoImageUrl } = useGetImageUrl(organization?.platformLogoImageId ?? null); - if (organizationIsError) { - return ; - } + const toast = useToast(); + + const [addedPlatformLogo, setAddedPlatformLogo] = useState(undefined); - if (organizationImagesIsLoading || !organization || organizationIsLoading) return ; + const { control, handleSubmit, reset } = useForm<{ platformDescription: string }>({ + resolver: yupResolver(platformDescriptionSchema), + defaultValues: { platformDescription: organization?.platformDescription ?? '' } + }); + const formKey = organization?.organizationId ?? 'loading'; - const handleFileUpload = async (files: File[], type: 'exploreAsGuest' | 'applyInterest') => { - const validFiles: File[] = []; - files.forEach((file) => { - if (file.size < MAX_FILE_SIZE) { - if (type === 'applyInterest') { - validFiles[0] = file; - } else if (type === 'exploreAsGuest') { - validFiles[1] = file; - } - } else { - toast.error(`Error uploading ${file.name}; file must be less than ${MAX_FILE_SIZE / 1024 / 1024} MB`, 5000); - } - }); + const onPlatformDescriptionSubmit = async (data: { platformDescription: string }) => { + try { + const updated = await setPlatformDescriptionMutation(data.platformDescription); + reset({ platformDescription: updated.platformDescription }); + toast.success('Platform description saved.'); + } catch (e) { + toast.error(e instanceof Error ? e.message : 'Failed to save platform description'); + } + }; - if (validFiles.length > 0) { - try { - type === 'applyInterest' ? setIsUploadingApply(true) : setIsUploadingExplore(true); - await organizationImages(validFiles); - toast.success('Image uploaded successfully!'); - } catch (error: any) { - if (organizationImagesIsError && organizationImagesError instanceof Error) { - toast.error(organizationImagesError.message); - } - } finally { - type === 'applyInterest' ? setIsUploadingApply(false) : setIsUploadingExplore(false); - } + const handlePlatformLogoUpload = async () => { + if (!addedPlatformLogo) return; + if (addedPlatformLogo.size >= MAX_FILE_SIZE) { + toast.error( + `Error uploading ${addedPlatformLogo.name}; file must be less than ${MAX_FILE_SIZE / 1024 / 1024} MB`, + 5000 + ); + return; + } + try { + await setPlatformLogoImage(addedPlatformLogo); + toast.success('Platform logo uploaded successfully.'); + setAddedPlatformLogo(undefined); + } catch (e) { + toast.error(e instanceof Error ? e.message : 'Failed to upload image'); } }; + if (organizationIsError) { + return ; + } + + if (!organization || organizationIsLoading) return ; + return ( @@ -91,85 +101,74 @@ const AdminToolsRecruitmentConfig: React.FC = () => { - Recruitment Images + Platform Logo - - - - Apply Interest Image - - {isUploadingApply ? ( - - - - ) : ( - <> - {!addedImage1 && applyInterestImageUrl && ( - - )} - { - if (e.target.files) { - setAddedImage1(e.target.files[0]); - } - }} - onSubmit={() => { - if (addedImage1) { - handleFileUpload([addedImage1], 'applyInterest'); - setAddedImage1(undefined); - } - }} - addedImage={addedImage1} - setAddedImage={setAddedImage1} - /> - - )} + {platformLogoLoading ? ( + + - + ) : ( - - Explore As Guest Image - - {isUploadingExplore ? ( - - - - ) : ( - <> - {!addedImage2 && exploreGuestImageUrl && ( - - )} - { - if (e.target.files) { - setAddedImage2(e.target.files[0]); - } - }} - onSubmit={() => { - if (addedImage2) { - handleFileUpload([addedImage2], 'exploreAsGuest'); - setAddedImage2(undefined); - } - }} - addedImage={addedImage2} - setAddedImage={setAddedImage2} - /> - + { + if (e.target.files?.[0]) setAddedPlatformLogo(e.target.files[0]); + }} + onSubmit={handlePlatformLogoUpload} + addedImage={addedPlatformLogo} + setAddedImage={setAddedPlatformLogo} + /> + {!addedPlatformLogo && platformLogoImageUrl && ( + )} - + )} + + + + Platform Description + +
{ + e.preventDefault(); + e.stopPropagation(); + handleSubmit(onPlatformDescriptionSubmit)(e); + }} + onKeyPress={(e) => { + e.key === 'Enter' && e.preventDefault(); + }} + > + + + + + + {platformDescriptionSaving ? 'Saving...' : 'Save'} + + +
diff --git a/src/frontend/src/pages/HomePage/GuestLandingPage.tsx b/src/frontend/src/pages/HomePage/GuestLandingPage.tsx deleted file mode 100644 index c9839917a3..0000000000 --- a/src/frontend/src/pages/HomePage/GuestLandingPage.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import { Typography, Box } from '@mui/material'; -import PageLayout from '../../components/PageLayout'; -import ImageWithButton from './components/ImageWithButton'; -import { useHistory } from 'react-router-dom'; -import { routes } from '../../utils/routes'; -import { useCurrentUser } from '../../hooks/users.hooks'; -import { useEffect } from 'react'; -import { useHomePageContext } from '../../app/HomePageContext'; -import { useCurrentOrganization } from '../../hooks/organizations.hooks'; -import LoadingIndicator from '../../components/LoadingIndicator'; -import ErrorPage from '../ErrorPage'; -import { useGetImageUrl } from '../../hooks/onboarding.hook'; - -const GuestHomePage = () => { - const user = useCurrentUser(); - const history = useHistory(); - const { - data: organization, - isLoading: organizationIsLoading, - isError: organizationIsError, - error: organizationError - } = useCurrentOrganization(); - const { setCurrentHomePage } = useHomePageContext(); - - const { - data: applyInterestImageUrl, - isLoading: applyImageLoading, - isError: applyImageIsError, - error: applyImageError - } = useGetImageUrl(organization?.applyInterestImageId ?? null); - const { - data: exploreGuestImageUrl, - isLoading: exploreImageLoading, - isError: exploreImageIsError, - error: exploreImageError - } = useGetImageUrl(organization?.exploreAsGuestImageId ?? null); - - useEffect(() => { - setCurrentHomePage('guest'); - }, [setCurrentHomePage]); - - if (organizationIsError) { - return ; - } - if (applyImageIsError) return ; - if (exploreImageIsError) return ; - - if (!organization || organizationIsLoading || applyImageLoading || exploreImageLoading) return ; - if (!applyInterestImageUrl || !exploreGuestImageUrl) return ; - - return ( - - - {user ? `Welcome, ${user.firstName}!` : 'Welcome, Guest!'} - - - - history.push(routes.HOME_PNM)} - /> - history.push(routes.HOME_MEMBER)} - /> - - - - ); -}; - -export default GuestHomePage; diff --git a/src/frontend/src/pages/HomePage/IntroGuestHomePage.tsx b/src/frontend/src/pages/HomePage/IntroGuestHomePage.tsx index 08e2bd763b..c7b9e44621 100644 --- a/src/frontend/src/pages/HomePage/IntroGuestHomePage.tsx +++ b/src/frontend/src/pages/HomePage/IntroGuestHomePage.tsx @@ -1,19 +1,19 @@ -import { Typography, Box } from '@mui/material'; -import PageLayout from '../../components/PageLayout'; -import ImageWithButton from './components/ImageWithButton'; -import { useHistory } from 'react-router-dom'; -import { routes } from '../../utils/routes'; -import { useCurrentUser } from '../../hooks/users.hooks'; +import { Typography, Box, Icon, Card, CardContent } from '@mui/material'; +import Link from '@mui/material/Link'; +import { Link as RouterLink } from 'react-router-dom'; import { useEffect } from 'react'; import { useHomePageContext } from '../../app/HomePageContext'; import { useCurrentOrganization } from '../../hooks/organizations.hooks'; import LoadingIndicator from '../../components/LoadingIndicator'; import ErrorPage from '../ErrorPage'; import { useGetImageUrl } from '../../hooks/onboarding.hook'; +import FeaturedProjects from './components/FeaturedProjects'; +import { useAllUsefulLinks } from '../../hooks/projects.hooks'; +import { Stack } from '@mui/system'; +import { routes } from '../../utils/routes'; +import { NERButton } from '../../components/NERButton'; const IntroGuestHomePage = () => { - const user = useCurrentUser(); - const history = useHistory(); const { data: organization, isLoading: organizationIsLoading, @@ -22,63 +22,126 @@ const IntroGuestHomePage = () => { } = useCurrentOrganization(); const { setCurrentHomePage } = useHomePageContext(); - const { - data: applyInterestImageUrl, - isLoading: applyImageLoading, - isError: applyImageIsError, - error: applyImageError - } = useGetImageUrl(organization?.applyInterestImageId ?? null); - const { - data: exploreGuestImageUrl, - isLoading: exploreImageLoading, - isError: exploreImageIsError, - error: exploreImageError - } = useGetImageUrl(organization?.exploreAsGuestImageId ?? null); - useEffect(() => { setCurrentHomePage('guest'); }, [setCurrentHomePage]); + const { + data: usefulLinks, + isLoading: usefulLinksIsLoading, + isError: usefulLinksIsError, + error: usefulLinksError + } = useAllUsefulLinks(); + + const { + data: finishlineImageUrl, + isLoading: finishlineImageIsLoading, + isError: finishlineImageIsError, + error: finishlineImageError + } = useGetImageUrl(organization?.platformLogoImageId ?? null); + if (organizationIsError) { return ; } - if (applyImageIsError) return ; - if (exploreImageIsError) return ; - if (!organization || organizationIsLoading || applyImageLoading || exploreImageLoading) return ; - if (!applyInterestImageUrl || !exploreGuestImageUrl) return ; + if (usefulLinksIsError) { + return ; + } + + if (finishlineImageIsError) { + return ; + } + + if ( + !finishlineImageUrl || + finishlineImageIsLoading || + !organization || + organizationIsLoading || + !usefulLinks || + usefulLinksIsLoading + ) + return ; return ( - - - {user ? `Welcome, ${user.firstName}!` : 'Welcome, Guest!'} + + + FinishLine By NER + + + + + {organization.platformDescription} + + + + {usefulLinks.map((link) => ( + + {link.linkType.iconName} + + ))} + + + - - history.push(routes.HOME_PNM)} - /> - history.push(routes.HOME_MEMBER)} - /> - + + + Are you a student interested in joining NER? + + + + Learn more + + + + + + + - +
); }; diff --git a/src/frontend/src/pages/HomePage/components/FeaturedProjects.tsx b/src/frontend/src/pages/HomePage/components/FeaturedProjects.tsx index 958e3f05f8..d706fe0315 100644 --- a/src/frontend/src/pages/HomePage/components/FeaturedProjects.tsx +++ b/src/frontend/src/pages/HomePage/components/FeaturedProjects.tsx @@ -10,7 +10,7 @@ import { wbsPipe } from 'shared'; import LoadingIndicator from '../../../components/LoadingIndicator'; import ScrollablePageBlock from './ScrollablePageBlock'; import EmptyPageBlockDisplay from './EmptyPageBlockDisplay'; -import { Box } from '@mui/material'; +import { Box, useMediaQuery, useTheme } from '@mui/material'; import { Error } from '@mui/icons-material'; const NoFeaturedProjectsDisplay: React.FC = () => { @@ -35,13 +35,15 @@ const NoFeaturedProjectsDisplay: React.FC = () => { }; const FeaturedProjects: React.FC = () => { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down('sm')); const { data: featuredProjects, isLoading, isError, error } = useFeaturedProjects(); if (isLoading || !featuredProjects) return ; if (isError) return ; const fullDisplay = ( - + {featuredProjects.length === 0 ? ( ) : ( diff --git a/src/frontend/src/utils/urls.ts b/src/frontend/src/utils/urls.ts index 316e21837d..fba3ddbc06 100644 --- a/src/frontend/src/utils/urls.ts +++ b/src/frontend/src/utils/urls.ts @@ -358,16 +358,18 @@ const organizations = () => `${API_URL}/organizations`; const currentOrganization = () => `${organizations()}/current`; const organizationsUsefulLinks = () => `${organizations()}/useful-links`; const organizationsSetUsefulLinks = () => `${organizationsUsefulLinks()}/set`; -const organizationsSetImages = () => `${organizations()}/images/update`; const organizationsUpdateContacts = () => `${organizations()}/contacts/set`; const organizationsSetOnboardingText = () => `${organizations()}/onboardingText/set`; const organizationsUpdateApplicationLink = () => `${organizations()}/application-link/update`; const organizationsSetDescription = () => `${organizations()}/description/set`; +const organizationsSetPlatformDescription = () => `${organizations()}/platform-description/set`; const organizationsFeaturedProjects = () => `${organizations()}/featured-projects`; const organizationsLogoImage = () => `${organizations()}/logo`; const organizationsSetLogoImage = () => `${organizations()}/logo/update`; const organizationsNewMemberImage = () => `${organizations()}/new-member-image`; const organizationsSetNewMemberImage = () => `${organizations()}/new-member-image/update`; +const organizationsPlatformLogoImage = () => `${organizations()}/platform-logo`; +const organizationsSetPlatformLogoImage = () => `${organizationsPlatformLogoImage()}/update`; const organizationsSetFeaturedProjects = () => `${organizationsFeaturedProjects()}/set`; const organizationsSetWorkspaceId = () => `${organizations()}/workspaceId/set`; const organizationsGetPartReviewGuideLink = () => `${organizations()}/part-review-guide-link/get`; @@ -719,16 +721,18 @@ export const apiUrls = { currentOrganization, organizationsUsefulLinks, organizationsSetUsefulLinks, - organizationsSetImages, organizationsUpdateContacts, organizationsSetOnboardingText, organizationsUpdateApplicationLink, organizationsFeaturedProjects, organizationsSetDescription, + organizationsSetPlatformDescription, organizationsLogoImage, organizationsSetLogoImage, organizationsNewMemberImage, organizationsSetNewMemberImage, + organizationsPlatformLogoImage, + organizationsSetPlatformLogoImage, organizationsSetFeaturedProjects, organizationsSetWorkspaceId, organizationsGetPartReviewGuideLink, diff --git a/src/shared/src/types/user-types.ts b/src/shared/src/types/user-types.ts index f0edc940cc..24cc141eac 100644 --- a/src/shared/src/types/user-types.ts +++ b/src/shared/src/types/user-types.ts @@ -48,9 +48,9 @@ export type OrganizationPreview = Pick< | 'dateDeleted' | 'description' | 'applicationLink' - | 'applyInterestImageId' - | 'exploreAsGuestImageId' | 'newMemberImageId' + | 'platformDescription' + | 'platformLogoImageId' >; export interface Organization { @@ -63,8 +63,6 @@ export interface Organization { treasurer?: User; advisor?: User; description: string; - applyInterestImageId?: string; - exploreAsGuestImageId?: string; newMemberImageId?: string; applicationLink?: string; onboardingText?: string; @@ -72,6 +70,8 @@ export interface Organization { slackWorkspaceId?: string; partReviewGuideLink?: string; sponsorshipNotificationsSlackChannelId?: string; + platformDescription: string; + platformLogoImageId?: string; } /**