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
new file mode 100644
index 00000000..27eab628
--- /dev/null
+++ b/libraries/nestjs-libraries/src/database/prisma/agencies/agencies.repository.ts
@@ -0,0 +1,154 @@
+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 _socialMediaAgenciesNiche: PrismaRepository<'socialMediaAgencyNiche'>
+ ) {}
+
+ getAllAgencies() {
+ return this._socialMediaAgencies.model.socialMediaAgency.findMany({
+ where: {
+ deletedAt: null,
+ approved: true,
+ },
+ include: {
+ logo: true,
+ niches: true,
+ },
+ });
+ }
+
+ getCount() {
+ return this._socialMediaAgencies.model.socialMediaAgency.count({
+ where: {
+ deletedAt: null,
+ approved: true,
+ },
+ });
+ }
+
+ getAllAgenciesSlug() {
+ return this._socialMediaAgencies.model.socialMediaAgency.findMany({
+ where: {
+ deletedAt: null,
+ approved: true,
+ },
+ select: {
+ slug: true,
+ },
+ });
+ }
+
+ getAgencyInformation(agency: string) {
+ return this._socialMediaAgencies.model.socialMediaAgency.findFirst({
+ where: {
+ slug: agency,
+ deletedAt: null,
+ approved: true,
+ },
+ include: {
+ logo: true,
+ niches: true,
+ },
+ });
+ }
+
+ getAgencyByUser(user: User) {
+ return this._socialMediaAgencies.model.socialMediaAgency.findFirst({
+ where: {
+ userId: user.id,
+ deletedAt: null,
+ },
+ include: {
+ logo: true,
+ niches: true,
+ },
+ });
+ }
+
+ 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.id,
+ shortDescription: body.shortDescription,
+ description: body.description,
+ approved: false,
+ },
+ 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.id,
+ shortDescription: body.shortDescription,
+ description: body.description,
+ slug: body.name.toLowerCase().replace(/ /g, '-'),
+ approved: false,
+ },
+ select: {
+ id: true,
+ },
+ });
+
+ await this._socialMediaAgenciesNiche.model.socialMediaAgencyNiche.deleteMany(
+ {
+ where: {
+ agencyId: insertAgency.id,
+ niche: {
+ notIn: body.niches,
+ },
+ },
+ }
+ );
+
+ const currentNiche =
+ await this._socialMediaAgenciesNiche.model.socialMediaAgencyNiche.findMany(
+ {
+ where: {
+ agencyId: insertAgency.id,
+ },
+ select: {
+ niche: true,
+ },
+ }
+ );
+
+ const addNewNiche = body.niches.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
new file mode 100644
index 00000000..0c0cd70c
--- /dev/null
+++ b/libraries/nestjs-libraries/src/database/prisma/agencies/agencies.service.ts
@@ -0,0 +1,165 @@
+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';
+import { EmailService } from '@gitroom/nestjs-libraries/services/email.service';
+
+@Injectable()
+export class AgenciesService {
+ constructor(
+ private _agenciesRepository: AgenciesRepository,
+ private _emailService: EmailService
+ ) {}
+ getAgencyByUser(user: User) {
+ return this._agenciesRepository.getAgencyByUser(user);
+ }
+
+ getCount() {
+ return this._agenciesRepository.getCount();
+ }
+
+ getAllAgencies() {
+ return this._agenciesRepository.getAllAgencies();
+ }
+
+ getAllAgenciesSlug() {
+ return this._agenciesRepository.getAllAgenciesSlug();
+ }
+
+ getAgencyInformation(agency: string) {
+ return this._agenciesRepository.getAgencyInformation(agency);
+ }
+
+ async createAgency(user: User, body: CreateAgencyDto) {
+ const agency = await this._agenciesRepository.createAgency(user, body);
+ await this._emailService.sendEmail(
+ 'nevo@postiz.com',
+ 'New agency created',
+ `
+
+
+
+
+
+
Email Template
+
+
+
+
+
+
+
+ `
+ );
+ return agency;
+ }
+}
diff --git a/libraries/nestjs-libraries/src/database/prisma/database.module.ts b/libraries/nestjs-libraries/src/database/prisma/database.module.ts
index 736c49b1..99b56385 100644
--- a/libraries/nestjs-libraries/src/database/prisma/database.module.ts
+++ b/libraries/nestjs-libraries/src/database/prisma/database.module.ts
@@ -27,6 +27,8 @@ import { MessagesRepository } from '@gitroom/nestjs-libraries/database/prisma/ma
import { StripeService } from '@gitroom/nestjs-libraries/services/stripe.service';
import { ExtractContentService } from '@gitroom/nestjs-libraries/openai/extract.content.service';
import { OpenaiService } from '@gitroom/nestjs-libraries/openai/openai.service';
+import { AgenciesService } from '@gitroom/nestjs-libraries/database/prisma/agencies/agencies.service';
+import { AgenciesRepository } from '@gitroom/nestjs-libraries/database/prisma/agencies/agencies.repository';
@Global()
@Module({
@@ -55,6 +57,8 @@ import { OpenaiService } from '@gitroom/nestjs-libraries/openai/openai.service';
MediaRepository,
CommentsRepository,
ItemUserRepository,
+ AgenciesService,
+ AgenciesRepository,
ItemUserService,
MessagesService,
CommentsService,
diff --git a/libraries/nestjs-libraries/src/database/prisma/schema.prisma b/libraries/nestjs-libraries/src/database/prisma/schema.prisma
index 1b87a0c8..079e0e74 100644
--- a/libraries/nestjs-libraries/src/database/prisma/schema.prisma
+++ b/libraries/nestjs-libraries/src/database/prisma/schema.prisma
@@ -62,6 +62,7 @@ model User {
orderSeller Orders[] @relation("orderSeller")
payoutProblems PayoutProblems[]
lastOnline DateTime @default(now())
+ agencies SocialMediaAgency[]
@@unique([email, providerName])
@@index([lastReadNotifications])
@@ -165,10 +166,51 @@ model Media {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
userPicture User[]
+ agencies SocialMediaAgency[]
@@index([organizationId])
}
+model SocialMediaAgency {
+ id String @id @default(uuid())
+ user User @relation(fields: [userId], references: [id])
+ userId String @unique()
+ name String
+ logoId String?
+ logo Media? @relation(fields: [logoId], references: [id])
+ website String?
+ slug String?
+
+ facebook String?
+ instagram String?
+ twitter String?
+ linkedIn String?
+ youtube String?
+ tiktok String?
+ otherSocialMedia String?
+
+ shortDescription String
+ description String
+ niches SocialMediaAgencyNiche[]
+ approved Boolean @default(false)
+
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ deletedAt DateTime?
+
+ @@index([userId])
+ @@index([deletedAt])
+ @@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..045f75df
--- /dev/null
+++ b/libraries/nestjs-libraries/src/dtos/agencies/create.agency.dto.ts
@@ -0,0 +1,59 @@
+import { ArrayMaxSize, ArrayMinSize, IsDefined, IsOptional, IsString, IsUrl, MinLength, ValidateIf } from 'class-validator';
+import { Type } from 'class-transformer';
+
+export class CreateAgencyLogoDto {
+ @IsString()
+ @IsDefined()
+ id: string;
+
+ path: string;
+}
+export class CreateAgencyDto {
+ @IsString()
+ @MinLength(3)
+ name: string;
+
+ @IsUrl()
+ @IsDefined()
+ website: string;
+
+ @IsUrl()
+ @ValidateIf((o) => o.facebook)
+ facebook: string;
+
+ @IsString()
+ @IsOptional()
+ instagram: string;
+
+ @IsString()
+ @IsOptional()
+ twitter: string;
+
+ @IsUrl()
+ @ValidateIf((o) => o.linkedIn)
+ linkedIn: string;
+
+ @IsUrl()
+ @ValidateIf((o) => o.youtube)
+ youtube: string;
+
+ @IsString()
+ @IsOptional()
+ tiktok: string;
+
+ @Type(() => CreateAgencyLogoDto)
+ logo: CreateAgencyLogoDto;
+
+ @IsString()
+ shortDescription: string;
+
+ @IsString()
+ description: string;
+
+ @IsString({
+ each: true
+ })
+ @ArrayMinSize(1)
+ @ArrayMaxSize(3)
+ niches: string[];
+}
\ No newline at end of file
diff --git a/libraries/nestjs-libraries/src/services/email.service.ts b/libraries/nestjs-libraries/src/services/email.service.ts
index b128d101..a847ed93 100644
--- a/libraries/nestjs-libraries/src/services/email.service.ts
+++ b/libraries/nestjs-libraries/src/services/email.service.ts
@@ -13,7 +13,7 @@ export class EmailService {
console.log('Sending email to', to);
const sends = await resend.emails.send({
- from: process.env.IS_GENERAL === 'true' ? 'Nevo
' : 'Nevo ',
+ from: process.env.IS_GENERAL === 'true' ? 'Nevo ' : 'Nevo ',
to,
subject,
html,