diff --git a/apps/backend/src/api/routes/billing.controller.ts b/apps/backend/src/api/routes/billing.controller.ts
index f14c8161..5bf7f73b 100644
--- a/apps/backend/src/api/routes/billing.controller.ts
+++ b/apps/backend/src/api/routes/billing.controller.ts
@@ -61,12 +61,14 @@ export class BillingController {
@Post('/cancel')
async cancel(
@GetOrgFromRequest() org: Organization,
+ @GetUserFromRequest() user: User,
@Body() body: { feedback: string }
) {
await this._notificationService.sendEmail(
process.env.EMAIL_FROM_ADDRESS,
'Subscription Cancelled',
- `Organization ${org.name} has cancelled their subscription because: ${body.feedback}`
+ `${user.name} from Organization ${org.name} has cancelled their subscription because: ${body.feedback}`,
+ user.email
);
return this._stripeService.setToCancel(org.id);
diff --git a/apps/backend/src/api/routes/media.controller.ts b/apps/backend/src/api/routes/media.controller.ts
index 6e7719f3..d9a0b906 100644
--- a/apps/backend/src/api/routes/media.controller.ts
+++ b/apps/backend/src/api/routes/media.controller.ts
@@ -1,5 +1,15 @@
import {
- Body, Controller, Get, Param, Post, Query, Req, Res, UploadedFile, UseInterceptors, UsePipes
+ Body,
+ Controller,
+ Get,
+ Param,
+ Post,
+ Query,
+ Req,
+ Res,
+ UploadedFile,
+ UseInterceptors,
+ UsePipes,
} from '@nestjs/common';
import { Request, Response } from 'express';
import { GetOrgFromRequest } from '@gitroom/nestjs-libraries/user/org.from.request';
@@ -25,14 +35,35 @@ export class MediaController {
async generateImage(
@GetOrgFromRequest() org: Organization,
@Req() req: Request,
- @Body('prompt') prompt: string
+ @Body('prompt') prompt: string,
+ isPicturePrompt = false
) {
const total = await this._subscriptionService.checkCredits(org);
if (total.credits <= 0) {
return false;
}
- return {output: 'data:image/png;base64,' + await this._mediaService.generateImage(prompt, org)};
+ return {
+ output:
+ (isPicturePrompt ? '' : 'data:image/png;base64,') +
+ (await this._mediaService.generateImage(prompt, org, isPicturePrompt)),
+ };
+ }
+
+ @Post('/generate-image-with-prompt')
+ async generateImageFromText(
+ @GetOrgFromRequest() org: Organization,
+ @Req() req: Request,
+ @Body('prompt') prompt: string
+ ) {
+ const image = await this.generateImage(org, req, prompt, true);
+ if (!image) {
+ return false;
+ }
+
+ const file = await this.storage.uploadSimple(image.output);
+
+ return this._mediaService.saveFile(org.id, file.split('/').pop(), file);
}
@Post('/upload-server')
@@ -43,7 +74,11 @@ export class MediaController {
@UploadedFile() file: Express.Multer.File
) {
const uploadedFile = await this.storage.uploadFile(file);
- return this._mediaService.saveFile(org.id, uploadedFile.originalname, uploadedFile.path);
+ return this._mediaService.saveFile(
+ org.id,
+ uploadedFile.originalname,
+ uploadedFile.path
+ );
}
@Post('/upload-simple')
@@ -53,7 +88,11 @@ export class MediaController {
@UploadedFile('file') file: Express.Multer.File
) {
const getFile = await this.storage.uploadFile(file);
- return this._mediaService.saveFile(org.id, getFile.originalname, getFile.path);
+ return this._mediaService.saveFile(
+ org.id,
+ getFile.originalname,
+ getFile.path
+ );
}
@Post('/:endpoint')
@@ -75,10 +114,14 @@ export class MediaController {
// @ts-ignore
const name = upload.Location.split('/').pop();
- // @ts-ignore
- const saveFile = await this._mediaService.saveFile(org.id, name, upload.Location);
+ const saveFile = await this._mediaService.saveFile(
+ org.id,
+ name,
+ // @ts-ignore
+ upload.Location
+ );
- res.status(200).json({...upload, saved: saveFile});
+ res.status(200).json({ ...upload, saved: saveFile });
// const filePath =
// file.path.indexOf('http') === 0
// ? file.path
diff --git a/apps/backend/src/api/routes/posts.controller.ts b/apps/backend/src/api/routes/posts.controller.ts
index e02f743e..1aea47c7 100644
--- a/apps/backend/src/api/routes/posts.controller.ts
+++ b/apps/backend/src/api/routes/posts.controller.ts
@@ -45,6 +45,12 @@ export class PostsController {
return this._messagesService.getMarketplaceAvailableOffers(org.id, id);
}
+ @Post('/posts/generate-image')
+ @CheckPolicies([AuthorizationActions.Create, Sections.POSTS_PER_MONTH])
+ generateImage(@Body() body: { text: string; type: string }) {
+
+ }
+
@Get('/')
async getPosts(
@GetOrgFromRequest() org: Organization,
diff --git a/apps/frontend/src/components/launches/add.edit.model.tsx b/apps/frontend/src/components/launches/add.edit.model.tsx
index 147b21b7..2f31271e 100644
--- a/apps/frontend/src/components/launches/add.edit.model.tsx
+++ b/apps/frontend/src/components/launches/add.edit.model.tsx
@@ -655,6 +655,7 @@ export const AddEditModal: FC<{
void;
+}> = (props) => {
+ const { value, onChange } = props;
+ const [loading, setLoading] = useState(false);
+ const fetch = useFetch();
+
+ const generateImage = useCallback(
+ (type: string) => async () => {
+ setLoading(true);
+ const image = await (
+ await fetch('/media/generate-image-with-prompt', {
+ method: 'POST',
+ body: JSON.stringify({
+ prompt: `
+
+${value}
+
+
+
+${type}
+
+
+`,
+ }),
+ })
+ ).json();
+ setLoading(false);
+ onChange(image);
+ },
+ [value, onChange]
+ );
+
+ return (
+
+
+ {value.length >= 30 && !loading && (
+
+
+ {list.map((p) => (
+ -
+ {p}
+
+ ))}
+
+
+ )}
+
+ );
+};
diff --git a/apps/frontend/src/components/launches/providers/high.order.provider.tsx b/apps/frontend/src/components/launches/providers/high.order.provider.tsx
index daa9d133..4f9d2be6 100644
--- a/apps/frontend/src/components/launches/providers/high.order.provider.tsx
+++ b/apps/frontend/src/components/launches/providers/high.order.provider.tsx
@@ -474,6 +474,7 @@ export const withProvider = function (
(
? undefined
: typeof maximumCharacters === 'number'
? maximumCharacters
- : maximumCharacters(JSON.parse(integration?.additionalSettings || '[]'))
+ : maximumCharacters(
+ JSON.parse(
+ integration?.additionalSettings || '[]'
+ )
+ )
}
/>
) : (
@@ -568,7 +573,11 @@ export const withProvider = function (
? undefined
: typeof maximumCharacters === 'number'
? maximumCharacters
- : maximumCharacters(JSON.parse(integration?.additionalSettings || '[]'))
+ : maximumCharacters(
+ JSON.parse(
+ integration?.additionalSettings || '[]'
+ )
+ )
}
/>
)
diff --git a/apps/frontend/src/components/media/media.component.tsx b/apps/frontend/src/components/media/media.component.tsx
index b44f5a22..d715ebb1 100644
--- a/apps/frontend/src/components/media/media.component.tsx
+++ b/apps/frontend/src/components/media/media.component.tsx
@@ -15,6 +15,7 @@ import { LoadingComponent } from '@gitroom/frontend/components/layout/loading';
import { MultipartFileUploader } from '@gitroom/frontend/components/media/new.uploader';
import dynamic from 'next/dynamic';
import { useUser } from '@gitroom/frontend/components/layout/user.context';
+import { AiImage } from '@gitroom/frontend/components/launches/ai.image';
const Polonto = dynamic(
() => import('@gitroom/frontend/components/launches/polonto')
);
@@ -152,7 +153,7 @@ export const MediaBox: FC<{
)}
>
{!mediaList.length && (
-
+
You don{"'"}t have any assets yet.
Click the button below to upload one
@@ -212,13 +213,14 @@ export const MultiMediaComponent: FC<{
label: string;
description: string;
value?: Array<{ path: string; id: string }>;
+ text: string;
name: string;
error?: any;
onChange: (event: {
target: { name: string; value?: Array<{ id: string; path: string }> };
}) => void;
}> = (props) => {
- const { name, label, error, description, onChange, value } = props;
+ const { name, label, error, text, description, onChange, value } = props;
const user = useUser();
useEffect(() => {
@@ -276,48 +278,59 @@ export const MultiMediaComponent: FC<{
onClick={showModal}
className="ml-[10px] rounded-[4px] mb-[10px] gap-[8px] !text-primary justify-center items-center w-[127px] flex border border-dashed border-customColor21 bg-input"
>
-
-
- Insert Media
+