Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add signatures #611

Merged
merged 1 commit into from
Feb 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions apps/backend/src/api/api.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { TrackService } from '@gitroom/nestjs-libraries/track/track.service';
import { ShortLinkService } from '@gitroom/nestjs-libraries/short-linking/short.link.service';
import { Nowpayments } from '@gitroom/nestjs-libraries/crypto/nowpayments';
import { WebhookController } from '@gitroom/backend/api/routes/webhooks.controller';
import { SignatureController } from '@gitroom/backend/api/routes/signature.controller';

const authenticatedController = [
UsersController,
Expand All @@ -44,11 +45,10 @@ const authenticatedController = [
CopilotController,
AgenciesController,
WebhookController,
SignatureController,
];
@Module({
imports: [
UploadModule,
],
imports: [UploadModule],
controllers: [
RootController,
StripeController,
Expand Down
39 changes: 39 additions & 0 deletions apps/backend/src/api/routes/signature.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Body, Controller, Get, Param, Post, Put } from '@nestjs/common';
import { GetOrgFromRequest } from '@gitroom/nestjs-libraries/user/org.from.request';
import { Organization } from '@prisma/client';
import { ApiTags } from '@nestjs/swagger';
import { SignatureService } from '@gitroom/nestjs-libraries/database/prisma/signatures/signature.service';
import { SignatureDto } from '@gitroom/nestjs-libraries/dtos/signature/signature.dto';

@ApiTags('Signatures')
@Controller('/signatures')
export class SignatureController {
constructor(private _signatureService: SignatureService) {}

@Get('/')
async getSignatures(@GetOrgFromRequest() org: Organization) {
return this._signatureService.getSignaturesByOrgId(org.id);
}

@Get('/default')
async getDefaultSignature(@GetOrgFromRequest() org: Organization) {
return (await this._signatureService.getDefaultSignature(org.id)) || {};
}

@Post('/')
async createSignature(
@GetOrgFromRequest() org: Organization,
@Body() body: SignatureDto
) {
return this._signatureService.createOrUpdateSignature(org.id, body);
}

@Put('/:id')
async updateSignature(
@Param('id') id: string,
@GetOrgFromRequest() org: Organization,
@Body() body: SignatureDto
) {
return this._signatureService.createOrUpdateSignature(org.id, body, id);
}
}
9 changes: 8 additions & 1 deletion apps/frontend/src/components/launches/calendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -490,7 +490,9 @@ export const CalendarColumn: FC<{
[integrations]
);

const addModal = useCallback(() => {
const addModal = useCallback(async () => {
const signature = await (await fetch('/signatures/default')).json();

modal.openModal({
closeOnClickOutside: false,
closeOnEscape: false,
Expand All @@ -503,6 +505,11 @@ export const CalendarColumn: FC<{
allIntegrations={integrations.map((p) => ({ ...p }))}
integrations={integrations.slice(0).map((p) => ({ ...p }))}
mutate={reloadCalendarView}
{...(signature?.id
? {
onlyValues: [{ content: '\n' + signature.content }],
}
: {})}
date={
randomHour ? getDate.hour(Math.floor(Math.random() * 24)) : getDate
}
Expand Down
2 changes: 2 additions & 0 deletions apps/frontend/src/components/launches/editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import { Theme } from 'emoji-picker-react';
import { BoldText } from '@gitroom/frontend/components/launches/bold.text';
import { UText } from '@gitroom/frontend/components/launches/u.text';
import { SignatureBox } from '@gitroom/frontend/components/signature';

export const Editor = forwardRef<
RefMDEditor,
Expand Down Expand Up @@ -61,6 +62,7 @@
return (
<>
<div className="flex gap-[5px] justify-end -mt-[30px]">
<SignatureBox editor={newRef?.current?.editor!} />

Check warning

Code scanning / ESLint

Disallow non-null assertions using the `!` postfix operator Warning

Forbidden non-null assertion.

Copilot Autofix AI 9 days ago

To fix the problem, we should avoid using the non-null assertion operator ! and instead handle the potential null or undefined values more gracefully. One way to do this is by using optional chaining and providing a fallback value or handling the case where the value is null or undefined.

In this specific case, we can check if newRef?.current?.editor is defined before using it. If it is not defined, we can either return early or provide a fallback behavior.

Suggested changeset 1
apps/frontend/src/components/launches/editor.tsx

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/apps/frontend/src/components/launches/editor.tsx b/apps/frontend/src/components/launches/editor.tsx
--- a/apps/frontend/src/components/launches/editor.tsx
+++ b/apps/frontend/src/components/launches/editor.tsx
@@ -54,4 +54,6 @@
         setTimeout(() => {
-          // @ts-ignore
-          Transforms.insertText(newRef?.current?.editor!, emoji);
+          const editor = newRef?.current?.editor;
+          if (editor) {
+            Transforms.insertText(editor, emoji);
+          }
         }, 10);
@@ -64,10 +66,17 @@
         <div className="flex gap-[5px] justify-end -mt-[30px]">
-          <SignatureBox editor={newRef?.current?.editor!} />
-          <UText
-            editor={newRef?.current?.editor!}
-            currentValue={props.value!}
-          />
-          <BoldText
-            editor={newRef?.current?.editor!}
-            currentValue={props.value!}
+          {newRef?.current?.editor && (
+            <SignatureBox editor={newRef.current.editor} />
+          )}
+          {newRef?.current?.editor && (
+            <UText
+              editor={newRef.current.editor}
+              currentValue={props.value ?? ''}
+            />
+          )}
+          {newRef?.current?.editor && (
+            <BoldText
+              editor={newRef.current.editor}
+              currentValue={props.value ?? ''}
+            />
+          )}
           />
EOF
@@ -54,4 +54,6 @@
setTimeout(() => {
// @ts-ignore
Transforms.insertText(newRef?.current?.editor!, emoji);
const editor = newRef?.current?.editor;
if (editor) {
Transforms.insertText(editor, emoji);
}
}, 10);
@@ -64,10 +66,17 @@
<div className="flex gap-[5px] justify-end -mt-[30px]">
<SignatureBox editor={newRef?.current?.editor!} />
<UText
editor={newRef?.current?.editor!}
currentValue={props.value!}
/>
<BoldText
editor={newRef?.current?.editor!}
currentValue={props.value!}
{newRef?.current?.editor && (
<SignatureBox editor={newRef.current.editor} />
)}
{newRef?.current?.editor && (
<UText
editor={newRef.current.editor}
currentValue={props.value ?? ''}
/>
)}
{newRef?.current?.editor && (
<BoldText
editor={newRef.current.editor}
currentValue={props.value ?? ''}
/>
)}
/>
Copilot is powered by AI and may make mistakes. Always verify output.
Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
<UText
editor={newRef?.current?.editor!}
currentValue={props.value!}
Expand Down
261 changes: 150 additions & 111 deletions apps/frontend/src/components/layout/settings.component.tsx

Large diffs are not rendered by default.

208 changes: 208 additions & 0 deletions apps/frontend/src/components/settings/signatures.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
import React, { FC, Fragment, useCallback } from 'react';
import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
import useSWR from 'swr';
import { Button } from '@gitroom/react/form/button';
import clsx from 'clsx';
import { useModals } from '@mantine/modals';
import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component';
import { array, boolean, object, string } from 'yup';

Check warning

Code scanning / ESLint

Disallow unused variables Warning

'array' is defined but never used.

Copilot Autofix AI 9 days ago

The best way to fix the problem is to remove the unused array import from the yup library. This will clean up the code and adhere to the ESLint rule that disallows unused variables. The change should be made in the file apps/frontend/src/components/settings/signatures.component.tsx on line 8.

Suggested changeset 1
apps/frontend/src/components/settings/signatures.component.tsx

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/apps/frontend/src/components/settings/signatures.component.tsx b/apps/frontend/src/components/settings/signatures.component.tsx
--- a/apps/frontend/src/components/settings/signatures.component.tsx
+++ b/apps/frontend/src/components/settings/signatures.component.tsx
@@ -7,3 +7,3 @@
 import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component';
-import { array, boolean, object, string } from 'yup';
+import { boolean, object, string } from 'yup';
 import { FormProvider, useForm } from 'react-hook-form';
EOF
@@ -7,3 +7,3 @@
import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component';
import { array, boolean, object, string } from 'yup';
import { boolean, object, string } from 'yup';
import { FormProvider, useForm } from 'react-hook-form';
Copilot is powered by AI and may make mistakes. Always verify output.
Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
import { FormProvider, useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import { CopilotTextarea } from '@copilotkit/react-textarea';
import { Select } from '@gitroom/react/form/select';
import { useToaster } from '@gitroom/react/toaster/toaster';

export const SignaturesComponent: FC<{
appendSignature?: (value: string) => void;
}> = (props) => {
const { appendSignature } = props;
const fetch = useFetch();
const modal = useModals();
const load = useCallback(async () => {
return (await fetch('/signatures')).json();
}, []);

Check warning

Code scanning / ESLint

verifies the list of dependencies for Hooks like useEffect and similar Warning

React Hook useCallback has a missing dependency: 'fetch'. Either include it or remove the dependency array.

const { data, mutate } = useSWR('signatures', load);

const addSignature = useCallback(
(data?: any) => () => {
modal.openModal({
title: '',
withCloseButton: false,
classNames: {
modal: 'bg-transparent text-textColor',
},
children: <AddOrRemoveSignature data={data} reload={mutate} />,
});
},
[mutate]

Check warning

Code scanning / ESLint

verifies the list of dependencies for Hooks like useEffect and similar Warning

React Hook useCallback has a missing dependency: 'modal'. Either include it or remove the dependency array.
);

return (
<div className="flex flex-col">
<h3 className="text-[20px]">Signatures</h3>
<div className="text-customColor18 mt-[4px]">
You can add signatures to your account to be used in your posts.
</div>
<div className="my-[16px] mt-[16px] bg-sixth border-fifth items-center border rounded-[4px] p-[24px] flex gap-[24px]">
<div className="flex flex-col w-full">
{!!data?.length && (
<div className={`grid ${!!appendSignature ? 'grid-cols-[1fr,1fr,1fr,1fr,1fr]': 'grid-cols-[1fr,1fr,1fr,1fr]'} w-full gap-y-[10px]`}>
<div>Content</div>
<div className="text-center">Auto Add?</div>
{!!appendSignature && <div className="text-center">Actions</div>}
<div className="text-center">Edit</div>
<div className="text-center">Delete</div>
{data?.map((p: any) => (
<Fragment key={p.id}>
<div className="relative flex-1 mr-[20px] overflow-x-hidden">
<div className="absolute left-0 line-clamp-1 top-[50%] -translate-y-[50%] text-ellipsis">
{p.content.slice(0,15) + '...'}
</div>
</div>
<div className="flex flex-col justify-center relative mr-[20px]">
<div className="text-center w-full absolute left-0 line-clamp-1 top-[50%] -translate-y-[50%]">
{p.autoAdd ? 'Yes' : 'No'}
</div>
</div>
{!!appendSignature && (
<div className="flex justify-center">
<Button onClick={() => appendSignature(p.content)}>Use Signature</Button>
</div>
)}
<div className="flex justify-center">
<div>
<Button onClick={addSignature(p)}>Edit</Button>
</div>
</div>
<div className="flex justify-center">
<div>
<Button onClick={addSignature(p)}>Delete</Button>
</div>
</div>
</Fragment>
))}
</div>
)}
<div>
<Button
onClick={addSignature()}
className={clsx((data?.length || 0) > 0 && 'my-[16px]')}
>
Add a signature
</Button>
</div>
</div>
</div>
</div>
);
};

const details = object().shape({
content: string().required(),
autoAdd: boolean().required(),
});

const AddOrRemoveSignature: FC<{ data?: any; reload: () => void }> = (
props
) => {
const { data, reload } = props;
const toast = useToaster();
const fetch = useFetch();

const form = useForm({
resolver: yupResolver(details),
values: {
content: data?.content || '',
autoAdd: data?.autoAdd || false,
},
});

const text = form.watch('content');
const autoAdd = form.watch('autoAdd');
const modal = useModals();

const callBack = useCallback(
async (values: any) => {
await fetch(data?.id ? `/signatures/${data.id}` : '/signatures', {
method: data?.id ? 'PUT' : 'POST',
body: JSON.stringify(values),
});

toast.show(
data?.id
? 'Webhook updated successfully'
: 'Webhook added successfully',
'success'
);

modal.closeModal(modal.modals[modal.modals.length - 1].id);
reload();
},
[data, modal]

Check warning

Code scanning / ESLint

verifies the list of dependencies for Hooks like useEffect and similar Warning

React Hook useCallback has missing dependencies: 'fetch', 'reload', and 'toast'. Either include them or remove the dependency array.
);

return (
<FormProvider {...form}>
<form onSubmit={form.handleSubmit(callBack)}>
<div className="relative flex gap-[20px] flex-col flex-1 rounded-[4px] border border-customColor6 bg-sixth p-[16px] pt-0 w-[500px]">
<TopTitle title={data ? 'Edit Signature' : 'Add Signature'} />
<button
className="outline-none absolute right-[20px] top-[15px] mantine-UnstyledButton-root mantine-ActionIcon-root hover:bg-tableBorder cursor-pointer mantine-Modal-close mantine-1dcetaa"
type="button"
onClick={() => modal.closeModal(modal.modals[modal.modals.length - 1].id)}
>
<svg
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
>
<path
d="M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z"
fill="currentColor"
fillRule="evenodd"
clipRule="evenodd"
></path>
</svg>
</button>

<div className="relative bg-customColor2">
<CopilotTextarea
disableBranding={true}
className={clsx(
'!min-h-40 !max-h-80 p-2 overflow-x-hidden scrollbar scrollbar-thumb-[#612AD5] bg-customColor2 outline-none'
)}
value={text}
onChange={(e) => {
form.setValue('content', e.target.value);
}}
placeholder="Write your signature..."
autosuggestionsConfig={{
textareaPurpose: `Assist me in writing social media signature`,
chatApiConfigs: {},
}}
/>
</div>

<Select
label="Auto add signature?"
{...form.register('autoAdd', {
setValueAs: (value) => value === 'true',
})}
>
<option value="false" selected={!autoAdd}>
No
</option>
<option value="true" selected={autoAdd}>
Yes
</option>
</Select>

<Button type="submit">Save</Button>
</div>
</form>
</FormProvider>
);
};
79 changes: 79 additions & 0 deletions apps/frontend/src/components/signature.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { FC, useCallback, useState } from 'react';
import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component';
import { SignaturesComponent } from '@gitroom/frontend/components/settings/signatures.component';
import { Transforms } from 'slate';

export const SignatureBox: FC<{ editor: any }> = ({ editor }) => {
const [showModal, setShowModal] = useState<any>(false);
const addSignature = useCallback(() => {
setShowModal(true);
}, [showModal]);

Check warning

Code scanning / ESLint

verifies the list of dependencies for Hooks like useEffect and similar Warning

React Hook useCallback has an unnecessary dependency: 'showModal'. Either exclude it or remove the dependency array.

const appendValue = (val: string) => {
Transforms.insertText(editor, "\n" + val);
setShowModal(false);
};

return (
<>
{showModal && (
<SignatureModal
appendSignature={appendValue}
close={() => setShowModal(false)}
/>
)}
<div
onClick={addSignature}
className="select-none cursor-pointer bg-customColor2 w-[40px] p-[5px] text-center rounded-tl-lg rounded-tr-lg"
>
<svg
width="25"
viewBox="0 0 30 28"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M28.0007 18.5H7.79447C8.06947 17.9475 8.34572 17.3825 8.61822 16.8087C8.64822 16.7475 8.67697 16.6837 8.70572 16.6262C10.4007 16.4262 12.3032 15.2187 14.3957 13.0137C15.062 14.5212 16.1545 16.1225 17.852 16.4437C18.5082 16.5687 19.5382 16.5425 20.7145 15.7175C21.1274 15.4227 21.5079 15.085 21.8495 14.71C22.8907 15.6625 24.7345 16.5 28.0007 16.5C28.3985 16.5 28.7801 16.3419 29.0614 16.0606C29.3427 15.7793 29.5007 15.3978 29.5007 15C29.5007 14.6021 29.3427 14.2206 29.0614 13.9393C28.7801 13.658 28.3985 13.5 28.0007 13.5C23.9182 13.5 23.5245 12.0487 23.5007 11.925C23.4832 11.5853 23.3508 11.2617 23.1252 11.0072C22.8996 10.7527 22.5942 10.5824 22.2591 10.5243C21.924 10.4661 21.5791 10.5236 21.2809 10.6873C20.9828 10.8509 20.7491 11.1111 20.6182 11.425C19.447 13.1662 18.6682 13.55 18.4107 13.5C17.797 13.3837 16.8545 11.5375 16.4707 9.70122C16.4097 9.40441 16.2602 9.13304 16.0419 8.92284C15.8237 8.71265 15.5468 8.5735 15.2479 8.5237C14.949 8.47391 14.6421 8.51581 14.3674 8.64389C14.0928 8.77197 13.8634 8.98022 13.7095 9.24122C12.4595 10.7887 11.3895 11.8575 10.4995 12.5537C13.1632 5.90497 12.6432 3.48872 11.9332 2.23247C11.3082 1.12372 10.1832 0.509973 8.76322 0.503723H8.72322C6.47322 0.519973 4.66947 2.58622 3.88197 6.03747C3.45072 7.92872 3.38197 10.0225 3.69947 11.78C4.02947 13.6162 4.74072 14.9887 5.77572 15.8075C5.32947 16.73 4.87572 17.6362 4.43197 18.5H2.00072C1.6029 18.5 1.22137 18.658 0.940062 18.9393C0.658758 19.2206 0.500722 19.6021 0.500722 20C0.500722 20.3978 0.658758 20.7793 0.940062 21.0606C1.22137 21.3419 1.6029 21.5 2.00072 21.5H2.83572C1.62572 23.7087 0.730722 25.2 0.710722 25.2262C0.60917 25.395 0.541869 25.5822 0.512665 25.7771C0.483461 25.9719 0.492925 26.1706 0.540517 26.3618C0.588109 26.5529 0.672896 26.7329 0.790035 26.8913C0.907175 27.0497 1.05437 27.1835 1.22322 27.285C1.45751 27.4272 1.72666 27.5016 2.00072 27.5C2.25956 27.5002 2.51405 27.4334 2.73944 27.3061C2.96483 27.1789 3.15346 26.9955 3.28697 26.7737C3.36447 26.6425 4.65322 24.5 6.25072 21.5H28.0007C28.3985 21.5 28.7801 21.3419 29.0614 21.0606C29.3427 20.7793 29.5007 20.3978 29.5007 20C29.5007 19.6021 29.3427 19.2206 29.0614 18.9393C28.7801 18.658 28.3985 18.5 28.0007 18.5ZM23.5007 12C23.5007 11.9775 23.5007 11.955 23.5007 11.9325C23.5026 11.9549 23.5026 11.9775 23.5007 12ZM6.80572 6.70122C7.22197 4.87497 8.05572 3.49997 8.75072 3.49997C9.20947 3.49997 9.28197 3.62497 9.32572 3.70497C9.50447 4.02247 10.1445 5.84122 7.14822 12.805C6.9073 12.3137 6.73968 11.7898 6.65072 11.25C6.40827 9.73793 6.46091 8.19325 6.80572 6.70122Z"
fill="currentColor"
/>
</svg>
</div>
</>
);
};

export const SignatureModal: FC<{
close: () => void;
appendSignature: (sign: string) => void;
}> = (props) => {
const { close, appendSignature } = props;
return (
<div className="bg-black/40 fixed left-0 top-0 w-full h-full z-[500]">
<div className="relative w-[900px] mx-auto flex gap-[20px] flex-col flex-1 rounded-[4px] border border-customColor6 bg-sixth p-[16px] pt-0">
<TopTitle title={`Add signature`} />
<button
className="outline-none absolute right-[20px] top-[15px] mantine-UnstyledButton-root mantine-ActionIcon-root hover:bg-tableBorder cursor-pointer mantine-Modal-close mantine-1dcetaa"
type="button"
>
<svg
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
onClick={close}
>
<path
d="M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z"
fill="currentColor"
fillRule="evenodd"
clipRule="evenodd"
></path>
</svg>
</button>

<SignaturesComponent appendSignature={appendSignature} />
</div>
</div>
);
};
Loading
Loading