diff --git a/apps/backend/src/api/routes/agencies.controller.ts b/apps/backend/src/api/routes/agencies.controller.ts
index 94ae5013..3b556cd1 100644
--- a/apps/backend/src/api/routes/agencies.controller.ts
+++ b/apps/backend/src/api/routes/agencies.controller.ts
@@ -1,8 +1,9 @@
-import { Controller, Get } from '@nestjs/common';
+import { Controller, Get, Post } from '@nestjs/common';
import { User } from '@prisma/client';
import { ApiTags } from '@nestjs/swagger';
import { AgenciesService } from '@gitroom/nestjs-libraries/database/prisma/agencies/agencies.service';
import { GetUserFromRequest } from '@gitroom/nestjs-libraries/user/user.from.request';
+import { CreateAgencyDto } from '@gitroom/nestjs-libraries/dtos/agencies/create.agency.dto';
@ApiTags('Agencies')
@Controller('/agencies')
@@ -12,4 +13,9 @@ export class AgenciesController {
async generateImage(@GetUserFromRequest() user: User) {
return this._agenciesService.getAgencyByUser(user);
}
+
+ @Post('/')
+ async createAgency(@GetUserFromRequest() user: User, body: CreateAgencyDto) {
+ return this._agenciesService.createAgency(user, body);
+ }
}
diff --git a/apps/frontend/src/app/auth/layout.tsx b/apps/frontend/src/app/auth/layout.tsx
index 2fe0c572..affad792 100644
--- a/apps/frontend/src/app/auth/layout.tsx
+++ b/apps/frontend/src/app/auth/layout.tsx
@@ -5,6 +5,8 @@ export const dynamic = 'force-dynamic';
import { ReactNode } from 'react';
import Image from 'next/image';
import clsx from 'clsx';
+import loadDynamic from 'next/dynamic';
+const ReturnUrlComponent = loadDynamic(() => import('./return.url.component'));
export default async function AuthLayout({
children,
@@ -13,6 +15,7 @@ export default async function AuthLayout({
}) {
return (
<>
+
diff --git a/apps/frontend/src/app/auth/return.url.component.tsx b/apps/frontend/src/app/auth/return.url.component.tsx
new file mode 100644
index 00000000..9e0303a8
--- /dev/null
+++ b/apps/frontend/src/app/auth/return.url.component.tsx
@@ -0,0 +1,27 @@
+'use client';
+import { useSearchParams } from 'next/navigation';
+import { useCallback, useEffect } from 'react';
+
+const ReturnUrlComponent = () => {
+ const params = useSearchParams();
+ const url = params.get('returnUrl');
+ useEffect(() => {
+ if (url?.indexOf?.('http')! > -1) {
+ localStorage.setItem('returnUrl', url!);
+ }
+ }, [url]);
+
+ return null;
+}
+
+export const useReturnUrl = () => {
+ return {
+ getAndClear: useCallback(() => {
+ const data = localStorage.getItem('returnUrl');
+ localStorage.removeItem('returnUrl');
+ return data;
+ }, [])
+ }
+}
+
+export default ReturnUrlComponent;
\ No newline at end of file
diff --git a/apps/frontend/src/components/layout/layout.context.tsx b/apps/frontend/src/components/layout/layout.context.tsx
index a44f94db..451bd408 100644
--- a/apps/frontend/src/components/layout/layout.context.tsx
+++ b/apps/frontend/src/components/layout/layout.context.tsx
@@ -4,6 +4,7 @@ import { ReactNode, useCallback } from 'react';
import { FetchWrapperComponent } from '@gitroom/helpers/utils/custom.fetch';
import { deleteDialog } from '@gitroom/react/helpers/delete.dialog';
import { isGeneral } from '@gitroom/react/helpers/is.general';
+import { useReturnUrl } from '@gitroom/frontend/app/auth/return.url.component';
export default function LayoutContext(params: { children: ReactNode }) {
if (params?.children) {
@@ -14,16 +15,33 @@ export default function LayoutContext(params: { children: ReactNode }) {
return <>>;
}
function LayoutContextInner(params: { children: ReactNode }) {
+ const returnUrl = useReturnUrl();
+
const afterRequest = useCallback(
async (url: string, options: RequestInit, response: Response) => {
+ const reloadOrOnboarding =
+ response?.headers?.get('reload') ||
+ response?.headers?.get('onboarding');
+
+ if (reloadOrOnboarding) {
+ const getAndClear = returnUrl.getAndClear();
+ if (getAndClear) {
+ window.location.href = getAndClear;
+ return true;
+ }
+ }
+
if (response?.headers?.get('onboarding')) {
window.location.href = isGeneral()
? '/launches?onboarding=true'
: '/analytics?onboarding=true';
+
+ return true;
}
if (response?.headers?.get('reload')) {
window.location.reload();
+ return true;
}
if (response.status === 401) {
diff --git a/libraries/nestjs-libraries/src/database/prisma/agencies/agencies.repository.ts b/libraries/nestjs-libraries/src/database/prisma/agencies/agencies.repository.ts
index 79a67f02..44860c3d 100644
--- a/libraries/nestjs-libraries/src/database/prisma/agencies/agencies.repository.ts
+++ b/libraries/nestjs-libraries/src/database/prisma/agencies/agencies.repository.ts
@@ -1,11 +1,13 @@
import { PrismaRepository } from '@gitroom/nestjs-libraries/database/prisma/prisma.service';
import { Injectable } from '@nestjs/common';
import { User } from '@prisma/client';
+import { CreateAgencyDto } from '@gitroom/nestjs-libraries/dtos/agencies/create.agency.dto';
@Injectable()
export class AgenciesRepository {
constructor(
- private _socialMediaAgencies: PrismaRepository<'socialMediaAgency'>
+ private _socialMediaAgencies: PrismaRepository<'socialMediaAgency'>,
+ private _socialMediaAgenciesNiche: PrismaRepository<'socialMediaAgencyNiche'>
) {}
getAgencyByUser(user: User) {
@@ -16,4 +18,87 @@ export class AgenciesRepository {
},
});
}
+
+ async createAgency(user: User, body: CreateAgencyDto) {
+ const insertAgency =
+ await this._socialMediaAgencies.model.socialMediaAgency.upsert({
+ where: {
+ userId: user.id,
+ },
+ update: {
+ userId: user.id,
+ name: body.name,
+ website: body.website,
+ facebook: body.facebook,
+ instagram: body.instagram,
+ twitter: body.twitter,
+ linkedIn: body.linkedin,
+ youtube: body.youtube,
+ tiktok: body.tiktok,
+ logoId: body.logo,
+ shortDescription: body.shortDescription,
+ description: body.description,
+ niches: {
+ create: body.niche.map((n) => ({
+ niche: n,
+ })),
+ },
+ },
+ create: {
+ userId: user.id,
+ name: body.name,
+ website: body.website,
+ facebook: body.facebook,
+ instagram: body.instagram,
+ twitter: body.twitter,
+ linkedIn: body.linkedin,
+ youtube: body.youtube,
+ tiktok: body.tiktok,
+ logoId: body.logo,
+ shortDescription: body.shortDescription,
+ description: body.description,
+ },
+ select: {
+ id: true,
+ },
+ });
+
+ await this._socialMediaAgenciesNiche.model.socialMediaAgencyNiche.deleteMany(
+ {
+ where: {
+ agencyId: insertAgency.id,
+ niche: {
+ notIn: body.niche,
+ },
+ },
+ }
+ );
+
+ const currentNiche =
+ await this._socialMediaAgenciesNiche.model.socialMediaAgencyNiche.findMany(
+ {
+ where: {
+ agencyId: insertAgency.id,
+ },
+ select: {
+ niche: true,
+ },
+ }
+ );
+
+ const addNewNiche = body.niche.filter(
+ (n) => !currentNiche.some((c) => c.niche === n)
+ );
+
+ await this._socialMediaAgenciesNiche.model.socialMediaAgencyNiche.createMany(
+ {
+ data: addNewNiche.map((n) => ({
+ agencyId: insertAgency.id,
+ niche: n,
+ })),
+ }
+ );
+
+ return insertAgency;
+ }
}
diff --git a/libraries/nestjs-libraries/src/database/prisma/agencies/agencies.service.ts b/libraries/nestjs-libraries/src/database/prisma/agencies/agencies.service.ts
index 3dd136dc..29d580ac 100644
--- a/libraries/nestjs-libraries/src/database/prisma/agencies/agencies.service.ts
+++ b/libraries/nestjs-libraries/src/database/prisma/agencies/agencies.service.ts
@@ -1,6 +1,7 @@
import { Injectable } from '@nestjs/common';
import { AgenciesRepository } from '@gitroom/nestjs-libraries/database/prisma/agencies/agencies.repository';
import { User } from '@prisma/client';
+import { CreateAgencyDto } from '@gitroom/nestjs-libraries/dtos/agencies/create.agency.dto';
@Injectable()
export class AgenciesService {
@@ -8,4 +9,8 @@ export class AgenciesService {
getAgencyByUser(user: User) {
return this._agenciesRepository.getAgencyByUser(user);
}
+
+ createAgency(user: User, body: CreateAgencyDto) {
+ return this._agenciesRepository.createAgency(user, body);
+ }
}
diff --git a/libraries/nestjs-libraries/src/database/prisma/schema.prisma b/libraries/nestjs-libraries/src/database/prisma/schema.prisma
index 41a59d39..35f8832d 100644
--- a/libraries/nestjs-libraries/src/database/prisma/schema.prisma
+++ b/libraries/nestjs-libraries/src/database/prisma/schema.prisma
@@ -174,14 +174,10 @@ model Media {
model SocialMediaAgency {
id String @id @default(uuid())
user User @relation(fields: [userId], references: [id])
- userId String
+ userId String @unique()
name String
logoId String?
logo Media? @relation(fields: [logoId], references: [id])
- tagline String?
- address String?
- phoneNumber String?
- emailAddress String?
website String?
facebook String?
@@ -189,30 +185,13 @@ model SocialMediaAgency {
twitter String?
linkedIn String?
youtube String?
+ tiktok String?
otherSocialMedia String?
- primaryServices String
- specialties String?
- packages String?
- caseStudies String?
-
- yearEstablished Int?
- teamSize Int?
- languagesSupported String?
- clientTypes String?
- clientTestimonials String?
-
- certifications String?
- awards String?
-
- blog String?
- faqs String?
- events String?
- freeConsultation Boolean @default(false)
-
- businessHours String?
- serviceArea String?
- termsAndConditions String?
+ shortDescription String
+ description String
+ niches SocialMediaAgencyNiche[]
+ approved Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -223,6 +202,14 @@ model SocialMediaAgency {
@@index([id])
}
+model SocialMediaAgencyNiche {
+ agencyId String
+ agency SocialMediaAgency @relation(fields: [agencyId], references: [id])
+ niche String
+
+ @@id([agencyId, niche])
+}
+
model Credits {
id String @id @default(uuid())
organization Organization @relation(fields: [organizationId], references: [id])
diff --git a/libraries/nestjs-libraries/src/dtos/agencies/create.agency.dto.ts b/libraries/nestjs-libraries/src/dtos/agencies/create.agency.dto.ts
new file mode 100644
index 00000000..3f6d62fd
--- /dev/null
+++ b/libraries/nestjs-libraries/src/dtos/agencies/create.agency.dto.ts
@@ -0,0 +1,43 @@
+import { IsDefined, IsString, IsUrl, MinLength } from 'class-validator';
+
+export class CreateAgencyDto {
+ @IsString()
+ @MinLength(3)
+ name: string;
+
+ @IsUrl()
+ @IsDefined()
+ website: string;
+
+ @IsUrl()
+ facebook: string;
+
+ @IsString()
+ instagram: string;
+
+ @IsString()
+ twitter: string;
+
+ @IsUrl()
+ linkedin: string;
+
+ @IsUrl()
+ youtube: string;
+
+ @IsUrl()
+ tiktok: string;
+
+ @IsString()
+ logo: string;
+
+ @IsString()
+ shortDescription: string;
+
+ @IsString()
+ description: string;
+
+ @IsString({
+ each: true
+ })
+ niche: string[];
+}
\ No newline at end of file