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

Feat(contact-tagging): Add support tagable contacts with bulk action #63

Merged
merged 56 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from 48 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
fbeb394
chore(cleanup): Removed unused isPartial tsdoc property
JumpLink Oct 29, 2024
1415ca7
feat(contact-tagging): Frontend: Contacts made selectable
JumpLink Oct 29, 2024
5b43972
feat(contact-tagging): Frontend: Initial tag manager page
JumpLink Oct 29, 2024
c27bc03
feat(contact-tagging): Tag translations centralised
JumpLink Oct 30, 2024
566c104
feat(contact-tagging): Moved pages/admin/callouts/ToggleTagButton.vue…
JumpLink Oct 30, 2024
75b40e3
chore(cleanup): Split utils index into multiple files
JumpLink Oct 30, 2024
d813187
feat(contact-tagging): Frontend: Add general tag manager
JumpLink Oct 30, 2024
1b7e661
feat(contact-tagging): Frontend: Show selected contact count
JumpLink Oct 31, 2024
d231a73
chore(doc): Update documentaion for new typeorm migration path
JumpLink Oct 31, 2024
3b18ba9
feat(contact-tagging): Backend: TypeORM contact model changes tags
JumpLink Oct 31, 2024
571f7eb
feat(contact-tagging): Backend: Adjust TypeORM migration file to migr…
JumpLink Oct 31, 2024
9c8cb2d
feat(contact-tagging): Backend: Initial working contact tag implement…
JumpLink Oct 31, 2024
1a1a0ce
Merge branch 'main' into feat/contact-tagging
JumpLink Nov 5, 2024
b1d46e7
fix(type): Due to the new rule ‘--isolatedDeclarations’
JumpLink Nov 5, 2024
7e9f917
feat(contact-tagging): Add model for ContactTagAssignment for more co…
JumpLink Nov 5, 2024
3f2ff9c
chore(doc): Moved deploy beabee on the server to root README.md
JumpLink Nov 6, 2024
85e711a
feat(contact-tagging): Frontend: Add support for tag filters
JumpLink Nov 6, 2024
1c6423c
feat(contact-tagging): Backend: Tag Controller with get, post and patch
JumpLink Nov 6, 2024
701cc5a
feat(contact-tagging): Backend: ContactTagService and delete controll…
JumpLink Nov 6, 2024
4773853
feat(contact-tagging): Merged TagTransformers
JumpLink Nov 7, 2024
8af97a6
feat(contact-tagging): Backend: Consolidate tag loading logic
JumpLink Nov 7, 2024
08f63bb
feat(contact-tagging): Backend: Moved loadEntityTags method to TagTra…
JumpLink Nov 7, 2024
6ea07e5
feat(contact-tagging): Backend: Moved ContactTagService methods to Ta…
JumpLink Nov 7, 2024
ee6947e
feat(contact-tagging): Backend: Adapt import steady script for the ne…
JumpLink Nov 7, 2024
c7377dd
chore(doc): Fixed some documentations
JumpLink Nov 7, 2024
49e16ce
feat(contact-tagging): Backend: Removed contact profile tag prop / col
JumpLink Nov 7, 2024
cc8d8af
chore(doc): Fixed formatting
JumpLink Nov 7, 2024
04768b8
feat(contact-tagging): Get batch contacts tags update working 🎉
JumpLink Nov 8, 2024
07b1ff9
feat(contact-tagging): Fixed contacts tags filter handler 😑
JumpLink Nov 8, 2024
aedb972
chore(doc): Add documentation for filter handlers
JumpLink Nov 8, 2024
d2a08d8
feat(contact-tagging): Backend: Tag filter handlers unified
JumpLink Nov 8, 2024
2227e2f
fix: Circular Dependencies
JumpLink Nov 8, 2024
b6c23c9
feat(contact-tagging): Backend: Cleanup tag filter handler method
JumpLink Nov 12, 2024
1925031
fix(api): Replace Partial<DTO> with concrete PartialDTO class
JumpLink Nov 12, 2024
f964c4f
feat(contact-tagging): Backend: Cleanup: Removed empty rules
JumpLink Nov 12, 2024
7267ac5
fix(api): Fix type checking for createPartialDTO<T>
JumpLink Nov 12, 2024
7a2c0c4
feat(contact-tagging): Frontend: Overview view for tags with the call…
JumpLink Nov 12, 2024
94a1816
fix(contact-tagging): Revert createPartialDTO and use a normal class …
JumpLink Nov 12, 2024
f43dbd2
feat(contact-tagging): Frontend: Tag toggle button aligned with exist…
JumpLink Nov 12, 2024
91d9a05
chore(contact-tagging): Resolved merge conflict
JumpLink Nov 12, 2024
efd03fa
fix(contact-tagging): Try to fix latest migration file
JumpLink Nov 13, 2024
5f9c356
feat(contact-tagging): Frontend: Make tags clickable
JumpLink Nov 13, 2024
471c0d8
chore(contact-tagging): Frontend: Cleanups
JumpLink Nov 13, 2024
c29392b
chore(contact-tagging): Backend: Cleanups
JumpLink Nov 13, 2024
e356e5a
review(contact-tagging): Cleanups and small fixes
JumpLink Nov 14, 2024
29440a7
fix(contact-tagging): Add error message for existing tags + translation
JumpLink Nov 14, 2024
d196736
fix(contact-tagging): Seperate tag remove warnings for contact and ca…
JumpLink Nov 14, 2024
1cc78b3
fix(contact-tagging): Tag Manager route from detail pages
JumpLink Nov 14, 2024
39951ba
fix(contact-tagging): Always enable ToggleTagButton to reach the Tag …
JumpLink Nov 14, 2024
a3b8abe
fix(contact-tagging): Frontend: Readd callout filter group
JumpLink Nov 14, 2024
268b8d4
fix(contact-tagging): Backend make DuplicateTagNameError more robust
JumpLink Nov 14, 2024
f1b37cf
fix(contact-tagging): Frontend: When the dropdown is open, the button…
JumpLink Nov 14, 2024
546c75d
fix(contact-tagging): Frontend: Restore selected items after toggled tag
JumpLink Nov 14, 2024
43e3e0f
fix(contact-tagging): Frontend: Wrap tags if there is not enough space
JumpLink Nov 14, 2024
06d0dd5
Merge branch 'main' into feat/contact-tagging
JumpLink Nov 14, 2024
418f3c3
feat(contact-tagging): Fetch tags ordered by name
JumpLink Nov 15, 2024
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: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ The Beabee Monorepo is organized into the following directories:

## Development Setup

> ⚠️⚠️⚠️ **WARNING** ⚠️⚠️⚠️
>
> If you want to deploy beabee on a server refer to
> [beabee/beabee-deploy](https://github.com/beabee-communityrm/beabee-deploy/)
> instead. The instructions below are for running beabee locally for development

To get started with local development, follow these steps:

### Prerequisites
Expand Down
38 changes: 9 additions & 29 deletions apps/backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,6 @@ Your input is invaluable to us as we continue to grow and improve beabee. Don't

## 💻 Install

> ⚠️⚠️⚠️ **WARNING** ⚠️⚠️⚠️
>
> If you want to deploy beabee on a server refer to
> [beabee/beabee-deploy](https://github.com/beabee-communityrm/beabee-deploy/)
> instead. The instructions below are for running beabee locally for development

You need:

- Docker >= 19.03.8
Expand Down Expand Up @@ -90,16 +84,12 @@ If you make changes to `.env` you need to recreate the Docker containers
docker compose -f ../../docker-compose.yml up -d
```

docker compose -f ../../docker-compose.yml up -d

````

If you change the dependencies in `package.json` you must rebuild and recreate the Docker containers

```bash
docker compose -f ../../docker-compose.yml build
docker compose -f ../../docker-compose.yml up -d
````
```

#### Generating database migrations

Expand All @@ -108,16 +98,16 @@ file. TypeORM will automatically generate a migration file based on your schema
changes

```bash
docker compose -f ../../docker-compose.yml start db
docker compose -f ../../docker-compose.yml run app yarn typeorm migration:generate src/migrations/MigrationName && yarn format
yarn build
docker compose -f ../../docker-compose.yml run app yarn typeorm migration:run
yarn typeorm:generate /opt/packages/core/src/migrations/<MigrationName> # The migration file is generated inside the docker container
yarn format # Formats the migration file
yarn build:core # Necessary for the new migration .js files to be found
yarn typeorm:migrate # Runs the migration
```

If you are still in the development phase, you may want to undo your last database migration as follows:

```bash
docker compose -f ../../docker-compose.yml run app yarn typeorm migration:revert
yarn typeorm:revert
```

To find out more about this topic, take a look at the [TypeORM Migration Guide](https://typeorm.io/migrations).
Expand All @@ -128,12 +118,10 @@ The codebase is broadly split into a few different parts

- **beabee core**

Shared between all services (API, webhooks and legacy)
The remaining files that have not yet been moved to the `@beabee/core` package, contains shared business logic between multiple services (API, webhooks and legacy)

```
./src/core
./src/models - Data models and database entities
./src/config - Config loader
```

- **API service**
Expand Down Expand Up @@ -168,14 +156,6 @@ Various tools for administration, including nightly cron jobs
./src/tools
```

- **Database migrations**

Autogenerated by TypeORM

```
./src/migrations
```

#### 📡 Webhooks

Webhooks are handled by the `webhook_app` service. This is a separate service from the API to allow for scaling independently.
Expand All @@ -195,7 +175,7 @@ Webhooks are handled by the `webhook_app` service. This is a separate service fr
By default we are using [MailDev](https://github.com/maildev/maildev) for local development. For this to work it must be configured the first time, run the following command if not already done:

```bash
docker compose -f ../../docker-compose.yml exec app node built/tools/configure
yarn setup:payment
```

If the Docker Compose Stack is started, you can reach MailDev via http://localhost:3025/ by default. If you now receive an e-mail during your tests, you will find it there.
Expand Down Expand Up @@ -224,7 +204,7 @@ BEABEE_STRIPE_SECRETKEY=<secret key>
And also that you have configured the payment methods using

```bash
docker compose -f ../../docker-compose.yml exec app node built/tools/configure
yarn setup:payment
```

You can get the public key and secret key in the [Stripe dashboard](https://dashboard.stripe.com).
Expand Down
11 changes: 9 additions & 2 deletions apps/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,22 @@
"dev:api:watch": "tsc-watch -p tsconfig.build.json --noClear --onSuccess 'docker compose -f ../../docker-compose.yml restart api_app router'",
"dev:api:logs": "yarn exec bash -c 'while true; do docker compose -f ../../docker-compose.yml logs -f --tail=0 api_app; sleep 1; done'",
"build": "rimraf built/ && gulp build && tsc -p ./tsconfig.build.json",
"build:core": "yarn workspace @beabee/core run build",
"generate:index": "ts-node scripts/generate-index.ts",
"watch": "tsc-watch -p tsconfig.build.json --noClear",
"check": "concurrently 'yarn:check:*'",
"check:tsc": "tsc --noEmit",
"check:prettier": "prettier -c .",
"check:dependencies": "dpdm -T --no-warning --no-tree --exit-code circular:1 src/app.ts src/webhooks/app.ts src/api/app.ts",
"format": "prettier --write .",
"setup": "yarn setup:db && yarn setup:user && yarn setup:payment",
"setup:user": "docker compose run --rm app node built/tools/new-user",
"setup:payment": "docker compose exec app node built/tools/configure",
"setup:db": "yarn typeorm:migrate",
"typeorm": "typeorm -d ./built/core/lib/typeorm.js",
"typeorm:migrate": "docker compose -f ../../docker-compose.yml up -d db && docker compose -f ../../docker-compose.yml run --rm app yarn typeorm migration:run"
"typeorm:migrate": "docker compose -f ../../docker-compose.yml up -d db app && docker compose -f ../../docker-compose.yml run --rm app npm run typeorm migration:run && yarn build:core",
"typeorm:revert": "docker compose -f ../../docker-compose.yml up -d db app && docker compose -f ../../docker-compose.yml run --rm app npm run typeorm migration:revert",
"typeorm:generate": "docker compose -f ../../docker-compose.yml up -d db app && docker compose -f ../../docker-compose.yml run --rm app npm run typeorm migration:generate --check"
},
"dependencies": {
"@beabee/beabee-common": "workspace:^",
Expand Down Expand Up @@ -139,4 +146,4 @@
"postcss": "8.4.32"
}
}
}
}
39 changes: 3 additions & 36 deletions apps/backend/src/api/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,7 @@ import {
useExpressServer
} from "routing-controllers";

import { ApiKeyController } from "./controllers/ApiKeyController";
import { AuthController } from "./controllers/AuthController";
import { CalloutController } from "./controllers/CalloutController";
import { CalloutResponseController } from "./controllers/CalloutResponseController";
import { CalloutResponseCommentController } from "./controllers/CalloutResponseCommentController";
import { ContentController } from "./controllers/ContentController";
import { EmailController } from "./controllers/EmailController";
import { ContactController } from "./controllers/ContactController";
import { NoticeController } from "./controllers/NoticeController";
import { PaymentController } from "./controllers/PaymentController";
import { SegmentController } from "./controllers/SegmentController";
import { SignupController } from "./controllers/SignupController";
import { StatsController } from "./controllers/StatsController";
import { ResetPasswordController } from "./controllers/ResetPasswordController";
import { ResetDeviceController } from "./controllers/ResetDeviceController";
import { RootController } from "./controllers/RootController";
import { UploadController } from "./controllers/UploadController";
import * as Controllers from "./controllers/index";

import { ValidateResponseInterceptor } from "./interceptors/ValidateResponseInterceptor";

Expand Down Expand Up @@ -71,25 +55,7 @@ initApp()

useExpressServer(app, {
routePrefix: "/1.0",
controllers: [
ApiKeyController,
AuthController,
CalloutController,
CalloutResponseController,
CalloutResponseCommentController,
ContentController,
EmailController,
ContactController,
NoticeController,
PaymentController,
SegmentController,
SignupController,
StatsController,
ResetPasswordController,
ResetDeviceController,
RootController,
UploadController
],
controllers: Object.values(Controllers),
interceptors: [ValidateResponseInterceptor],
middlewares: [AuthMiddleware],
currentUserChecker,
Expand Down Expand Up @@ -126,6 +92,7 @@ initApp()
log.notice("Bad request, probably a validation error", {
body: req.body,
query: req.query,
stack: error.stack,
error
});
}
Expand Down
27 changes: 12 additions & 15 deletions apps/backend/src/api/controllers/CalloutController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import { CalloutId } from "@api/decorators/CalloutId";
import { CurrentAuth } from "@api/decorators/CurrentAuth";
import PartialBody from "@api/decorators/PartialBody";
import { InvalidCalloutResponse, UnauthorizedError } from "@beabee/core/errors";
import CalloutTagTransformer from "@api/transformers/CalloutTagTransformer";
import { calloutTagTransformer } from "@api/transformers/TagTransformer";
import CalloutTransformer from "@api/transformers/CalloutTransformer";
import CalloutResponseExporter from "@api/transformers/CalloutResponseExporter";
import CalloutResponseMapTransformer from "@api/transformers/CalloutResponseMapTransformer";
Expand Down Expand Up @@ -207,13 +207,14 @@ export class CalloutController {
}
}

// TODO: move to CalloutTagController like we did for contact tags?
@Authorized("admin")
@Get("/:id/tags")
async getCalloutTags(
@CurrentAuth({ required: true }) auth: AuthInfo,
@CalloutId() id: string
): Promise<GetCalloutTagDto[]> {
const result = await CalloutTagTransformer.fetch(auth, {
const result = await calloutTagTransformer.fetch(auth, {
rules: {
condition: "AND",
rules: [{ field: "calloutId", operator: "equal", value: [id] }]
Expand All @@ -223,6 +224,7 @@ export class CalloutController {
return result.items;
}

// TODO: move to CalloutTagController like we did for contact tags?
@Authorized("admin")
@Post("/:id/tags")
async createCalloutTag(
Expand All @@ -235,46 +237,41 @@ export class CalloutController {
description: data.description,
calloutId: id
});

return CalloutTagTransformer.convert(tag);
return calloutTagTransformer.convert(tag);
}

// TODO: move to CalloutTagController like we did for contact tags?
@Authorized("admin")
@Get("/:id/tags/:tagId")
async getCalloutTag(
@CurrentAuth({ required: true }) auth: AuthInfo,
@Param("tagId") tagId: string
): Promise<GetCalloutTagDto | undefined> {
return CalloutTagTransformer.fetchOneById(auth, tagId);
return calloutTagTransformer.fetchOneById(auth, tagId);
}

// TODO: move to CalloutTagController like we did for contact tags?
@Authorized("admin")
@Patch("/:id/tags/:tagId")
async updateCalloutTag(
@CurrentAuth({ required: true }) auth: AuthInfo,
@CalloutId() id: string,
@Param("tagId") tagId: string,
@PartialBody() data: CreateCalloutTagDto // Partial<CreateCalloutTagData>
@PartialBody() data: CreateCalloutTagDto // Partial<TagCreateData>
): Promise<GetCalloutTagDto | undefined> {
await getRepository(CalloutTag).update({ id: tagId, calloutId: id }, data);

return CalloutTagTransformer.fetchOneById(auth, tagId);
return calloutTagTransformer.fetchOneById(auth, tagId);
}

// TODO: move to CalloutTagController like we did for contact tags?
@Authorized("admin")
@Delete("/:id/tags/:tagId")
@OnUndefined(204)
async deleteCalloutTag(
@CalloutId() id: string,
@Param("tagId") tagId: string
): Promise<void> {
await getRepository(CalloutResponseTag).delete({ tag: { id: tagId } });
const result = await getRepository(CalloutTag).delete({
calloutId: id,
id: tagId
});
if (result.affected === 0) {
throw new NotFoundError();
}
await calloutTagTransformer.delete(tagId, CalloutResponseTag);
}
}
4 changes: 2 additions & 2 deletions apps/backend/src/api/controllers/CalloutResponseController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { UUIDParams } from "@api/params/UUIDParams";
import {
BatchUpdateCalloutResponseDto,
BatchUpdateCalloutResponseResultDto,
CreateCalloutResponseDto,
UpdateCalloutResponseDto,
GetCalloutResponseDto,
GetCalloutResponseOptsDto,
ListCalloutResponsesDto
Expand Down Expand Up @@ -58,7 +58,7 @@ export class CalloutResponseController {
async updateCalloutResponse(
@CurrentAuth({ required: true }) auth: AuthInfo,
@Params() { id }: UUIDParams,
@PartialBody() data: CreateCalloutResponseDto // Should be Partial<CreateCalloutResponseData>
@PartialBody() data: UpdateCalloutResponseDto
): Promise<GetCalloutResponseDto | undefined> {
await CalloutResponseTransformer.updateOneById(auth, id, data);
return await CalloutResponseTransformer.fetchOneById(auth, id);
Expand Down
Loading
Loading