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: add an ability to mark contributors as hidden gf-346 #400

Merged
merged 36 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
f419a30
feat: add new icons gf-346
kolibri753 Sep 19, 2024
ead4874
feat: add is_hidden column to contributors table gf-346
kolibri753 Sep 19, 2024
fd50a2d
feat: add is_hidden to the table and adjust backend gf-346
kolibri753 Sep 19, 2024
e1aa127
feat: exclude hidden contributors from project contributors page gf-346
kolibri753 Sep 19, 2024
29b1145
refactor: add isHiddenHeader component gf-346
kolibri753 Sep 19, 2024
2007813
refactor: rename visibleContributors into visibleProjectContributors …
kolibri753 Sep 19, 2024
e16be20
Merge branch 'main' into 346-feat-add-an-ability-to-mark-contributors…
kolibri753 Sep 19, 2024
59ea2c7
feat: add checkbox to the edit modal gf-346
kolibri753 Sep 19, 2024
e0df714
Merge branch 'main' into 346-feat-add-an-ability-to-mark-contributors…
kolibri753 Sep 19, 2024
9bc81dd
Merge branch 'main' into 346-feat-add-an-ability-to-mark-contributors…
kolibri753 Sep 19, 2024
8d35b9a
Merge branch 'main' into 346-feat-add-an-ability-to-mark-contributors…
kolibri753 Sep 20, 2024
09d059f
feat: filter logs on analytics page based on hidden contributors gf-346
kolibri753 Sep 20, 2024
999e8f0
feat: simplify isHidden header by changing to Do Not Track gf-346
kolibri753 Sep 22, 2024
cbae90c
feat: remove circle-question icon gf-346
kolibri753 Sep 22, 2024
06c1af3
refactor: update findAllByProjectId to handle filtering hidden contri…
kolibri753 Sep 22, 2024
85804d2
refactor: update findAll in activity-log service to handle hidden con…
kolibri753 Sep 22, 2024
13b4cb6
refactor: wrap return in curly brakets gf-346
kolibri753 Sep 22, 2024
2fb866c
Merge branch 'main' into 346-feat-add-an-ability-to-mark-contributors…
kolibri753 Sep 22, 2024
c7ee254
refactor: group contributor-related fields under contributor gf-346
kolibri753 Sep 23, 2024
a3f55f9
refactor: add isHidden to findAll in repository gf-346
kolibri753 Sep 23, 2024
2dd50cc
Merge branch 'main' into 346-feat-add-an-ability-to-mark-contributors…
kolibri753 Sep 23, 2024
495e46b
refactor: remove JSX.Element in cell for consistency gf-346
kolibri753 Sep 23, 2024
8738c5c
refactor: simplify isHidden contributors check gf-346
kolibri753 Sep 23, 2024
ce1581b
refactor: remove check and use hasHidden instead gf-346
kolibri753 Sep 23, 2024
0f6754b
Merge branch 'main' into 346-feat-add-an-ability-to-mark-contributors…
kolibri753 Sep 23, 2024
8b9c75d
refactor: make hasHidden true by default and update findAllByProjectI…
kolibri753 Sep 24, 2024
e43e642
Merge branch 'main' into 346-feat-add-an-ability-to-mark-contributors…
kolibri753 Sep 24, 2024
170e8c5
Merge branch 'main' into 346-feat-add-an-ability-to-mark-contributors…
kolibri753 Sep 24, 2024
f95fb50
refactor: use hiddenAt instead of isHidden gf-346
kolibri753 Sep 24, 2024
c27f689
fix: add useFormWatch to be able to see value from checkbox gf-346
kolibri753 Sep 24, 2024
f3f1474
fix: use dateTime instead of timestamp gf-346
kolibri753 Sep 24, 2024
7152a81
refactor: improve code readability and fix small typos gf-346
kolibri753 Sep 24, 2024
851eab1
refactor: update patch types by changing hiddenAt to isHidden gf-346
kolibri753 Sep 24, 2024
c851970
feat: add hidden_at column to contributors table in database schema g…
kolibri753 Sep 24, 2024
02651c2
refactor: simplify handleSubmit in handleFormSubmit gf-346
kolibri753 Sep 25, 2024
70a4679
fix: change isHidden ot hiddenAt in ContributorPatchResponseDto gf-346
kolibri753 Sep 25, 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { type Knex } from "knex";

const TABLE_NAME = "contributors";

const ColumnName = {
HIDDEN_AT: "hidden_at",
} as const;

function up(knex: Knex): Promise<void> {
return knex.schema.alterTable(TABLE_NAME, (table) => {
table.dateTime(ColumnName.HIDDEN_AT).nullable().defaultTo(null);
});
}

function down(knex: Knex): Promise<void> {
return knex.schema.alterTable(TABLE_NAME, (table) => {
table.dropColumn(ColumnName.HIDDEN_AT);
});
}

export { down, up };
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,12 @@ class ActivityLogRepository implements Repository {
}: ActivityLogQueryParameters): Promise<{ items: ActivityLogEntity[] }> {
const query = this.activityLogModel
.query()
.withGraphFetched("[gitEmail.contributor, project, createdByUser]")
.withGraphFetched("[project, createdByUser]")
.withGraphJoined("gitEmail.contributor")
.modifyGraph("gitEmail.contributor", (builder) => {
builder.select("id", "name");
builder.select("id", "name", "hiddenAt");
})
.whereNull("gitEmail:contributor.hiddenAt")
.whereBetween("activity_logs.date", [startDate, endDate])
.orderBy("date");

Expand Down
14 changes: 10 additions & 4 deletions apps/backend/src/modules/activity-logs/activity-log.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,11 @@ class ActivityLogService implements Service {
);

const allContributors = await (projectId
? this.contributorService.findAllByProjectId(Number(projectId))
: this.contributorService.findAllWithoutPagination());
? this.contributorService.findAllByProjectId({
hasHidden: false,
projectId: Number(projectId),
})
: this.contributorService.findAllWithoutPagination({ hasHidden: false }));

const dateRange = getDateRange(startDate, endDate);

Expand Down Expand Up @@ -184,8 +187,11 @@ class ActivityLogService implements Service {

return {
commitsNumber: commitsArray,
contributorId: contributorId ?? "",
contributorName: contributorName ?? "",
contributor: {
hiddenAt: null,
id: contributorId ?? "",
name: contributorName ?? "",
},
};
}),
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,10 @@ class ContributorController extends BaseController {
const projectId = Number(options.query.projectId);

return {
payload: await this.contributorService.findAllByProjectId(projectId),
payload: await this.contributorService.findAllByProjectId({
hasHidden: true,
projectId,
}),
status: HTTPCode.OK,
};
}
Expand Down
17 changes: 15 additions & 2 deletions apps/backend/src/modules/contributors/contributor.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,46 +4,53 @@ import { type ContributorGetAllItemResponseDto } from "./libs/types/types.js";

class ContributorEntity implements Entity {
private gitEmails: { email: string; id: number }[];
private hiddenAt: null | string;
private id: null | number;
private lastActivityDate: null | string;
private name: string;
private projects: { id: number; name: string }[];

private constructor({
gitEmails,
hiddenAt,
id,
lastActivityDate,
name,
projects,
}: {
gitEmails: { email: string; id: number }[];
hiddenAt: null | string;
id: null | number;
lastActivityDate: null | string;
name: string;
projects: { id: number; name: string }[];
}) {
this.gitEmails = gitEmails;
this.id = id;
this.name = name;
this.hiddenAt = hiddenAt;
this.lastActivityDate = lastActivityDate;
this.gitEmails = gitEmails;
this.name = name;
this.projects = projects;
}

public static initialize({
gitEmails,
hiddenAt,
id,
lastActivityDate,
name,
projects,
}: {
gitEmails: { email: string; id: number }[];
hiddenAt: null | string;
id: number;
lastActivityDate: null | string;
name: string;
projects: { id: number; name: string }[];
}): ContributorEntity {
return new ContributorEntity({
gitEmails,
hiddenAt,
id,
lastActivityDate,
name,
Expand All @@ -53,15 +60,18 @@ class ContributorEntity implements Entity {

public static initializeNew({
gitEmails = [],
hiddenAt = null,
name,
projects = [],
}: {
gitEmails?: { email: string; id: number }[];
hiddenAt?: null | string;
name: string;
projects?: { id: number; name: string }[];
}): ContributorEntity {
return new ContributorEntity({
gitEmails,
hiddenAt,
id: null,
lastActivityDate: null,
name,
Expand All @@ -71,12 +81,14 @@ class ContributorEntity implements Entity {

public toNewObject(): {
gitEmails: { email: string; id: number }[];
hiddenAt: null | string;
lastActivityDate: null | string;
name: string;
projects: { id: number; name: string }[];
} {
return {
gitEmails: this.gitEmails,
hiddenAt: this.hiddenAt,
lastActivityDate: this.lastActivityDate,
name: this.name,
projects: this.projects,
Expand All @@ -86,6 +98,7 @@ class ContributorEntity implements Entity {
public toObject(): ContributorGetAllItemResponseDto {
return {
gitEmails: this.gitEmails,
hiddenAt: this.hiddenAt,
id: this.id as number,
lastActivityDate: this.lastActivityDate,
name: this.name,
Expand Down
5 changes: 5 additions & 0 deletions apps/backend/src/modules/contributors/contributor.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,13 @@ import { type ProjectModel } from "../projects/project.model.js";

class ContributorModel extends AbstractModel {
public gitEmails!: GitEmailModel[];

public hiddenAt!: null | string;

public lastActivityDate!: null | string;

public name!: string;

public projects!: ProjectModel[];

public static override get relationMappings(): RelationMappings {
Expand Down
50 changes: 41 additions & 9 deletions apps/backend/src/modules/contributors/contributor.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,13 @@ class ContributorRepository implements Repository {
}

public async findAll({
hasHidden = true,
page,
pageSize,
}: PaginationQueryParameters): Promise<
}: { hasHidden?: boolean } & PaginationQueryParameters): Promise<
Vitaliistf marked this conversation as resolved.
Show resolved Hide resolved
PaginationResponseDto<ContributorEntity>
> {
const { results, total } = await this.contributorModel
const query = this.contributorModel
.query()
.orderBy("createdAt", SortType.DESCENDING)
.page(page, pageSize)
Expand All @@ -77,6 +78,12 @@ class ContributorRepository implements Repository {
.groupBy("contributors.id")
.withGraphFetched("gitEmails");

if (!hasHidden) {
query.whereNull("contributors.hiddenAt");
}

const { results, total } = await query;

return {
items: results.map((contributor) => {
return ContributorEntity.initialize(contributor);
Expand All @@ -85,10 +92,14 @@ class ContributorRepository implements Repository {
};
}

public async findAllByProjectId(
projectId: number,
): Promise<{ items: ContributorEntity[] }> {
const contributorsWithProjectsAndEmails = await this.contributorModel
public async findAllByProjectId({
hasHidden = true,
projectId,
}: {
hasHidden?: boolean;
projectId: number;
}): Promise<{ items: ContributorEntity[] }> {
const query = this.contributorModel
.query()
.select("contributors.*")
.select(
Expand All @@ -105,20 +116,31 @@ class ContributorRepository implements Repository {
.leftJoin("activity_logs", "git_emails.id", "activity_logs.git_email_id")
.leftJoin("projects", "activity_logs.project_id", "projects.id")
.where("projects.id", projectId)
.whereNull("contributors.hiddenAt")
.groupBy("contributors.id")
.withGraphFetched("gitEmails");

if (!hasHidden) {
query.whereNull("contributors.hiddenAt");
}

const contributorsWithProjectsAndEmails = await query;

return {
items: contributorsWithProjectsAndEmails.map((contributor) => {
return ContributorEntity.initialize(contributor);
}),
};
}

public async findAllWithoutPagination(): Promise<{
public async findAllWithoutPagination({
hasHidden = true,
}: {
hasHidden?: boolean;
}): Promise<{
items: ContributorEntity[];
}> {
const results = await this.contributorModel
const query = this.contributorModel
.query()
.select("contributors.*")
.select(
Expand All @@ -132,6 +154,12 @@ class ContributorRepository implements Repository {
.groupBy("contributors.id")
.withGraphFetched("gitEmails");

if (!hasHidden) {
query.whereNull("contributors.hiddenAt");
}

const results = await query;

return {
items: results.map((contributor) => {
return ContributorEntity.initialize(contributor);
Expand Down Expand Up @@ -208,9 +236,13 @@ class ContributorRepository implements Repository {
contributorId: number,
data: ContributorPatchRequestDto,
): Promise<ContributorEntity | null> {
const hiddenAt = data.isHidden ? new Date().toISOString() : null;
const contributor = await this.contributorModel
.query()
.patchAndFetchById(contributorId, { name: data.name });
.patchAndFetchById(contributorId, {
hiddenAt,
name: data.name,
});

return ContributorEntity.initialize(contributor);
}
Expand Down
29 changes: 21 additions & 8 deletions apps/backend/src/modules/contributors/contributor.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,10 @@ class ContributorService implements Service {
}

public async findAll(
parameters: PaginationQueryParameters,
parameters: { hasHidden?: boolean } & PaginationQueryParameters,
): Promise<ContributorGetAllResponseDto> {
const contributors = await this.contributorRepository.findAll({
hasHidden: parameters.hasHidden ?? true,
page: parameters.page - PAGE_INDEX_OFFSET,
pageSize: parameters.pageSize,
});
Expand All @@ -84,11 +85,17 @@ class ContributorService implements Service {
};
}

public async findAllByProjectId(
projectId: number,
): Promise<ContributorGetAllResponseDto> {
const contributors =
await this.contributorRepository.findAllByProjectId(projectId);
public async findAllByProjectId({
hasHidden = true,
projectId,
}: {
hasHidden?: boolean;
projectId: number;
}): Promise<ContributorGetAllResponseDto> {
const contributors = await this.contributorRepository.findAllByProjectId({
hasHidden,
projectId,
});

return {
items: contributors.items.map((item) => {
Expand All @@ -106,9 +113,15 @@ class ContributorService implements Service {
};
}

public async findAllWithoutPagination(): Promise<ContributorGetAllResponseDto> {
public async findAllWithoutPagination({
hasHidden,
}: {
hasHidden?: boolean;
}): Promise<ContributorGetAllResponseDto> {
const contributors =
await this.contributorRepository.findAllWithoutPagination();
await this.contributorRepository.findAllWithoutPagination({
hasHidden: hasHidden ?? true,
});

return {
items: contributors.items.map((item) => {
Expand Down
3 changes: 3 additions & 0 deletions apps/frontend/src/assets/images/icons/check.svg
GvoFor marked this conversation as resolved.
Show resolved Hide resolved
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions apps/frontend/src/libs/components/checkbox/checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ import styles from "./styles.module.css";
type Properties<T extends FieldValues> = {
control: Control<T, null>;
errors: FieldErrors<T>;
id?: string;
name: FieldPath<T>;
};

const Checkbox = <T extends FieldValues>({
control,
errors,
id,
name,
}: Properties<T>): JSX.Element => {
const { field } = useFormController({ control, name });
Expand All @@ -30,6 +32,7 @@ const Checkbox = <T extends FieldValues>({
<input
className={styles["checkbox"]}
defaultChecked={field.value}
id={id}
name={name}
onChange={field.onChange}
type="checkbox"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { type FC } from "react";
import Access from "~/assets/images/icons/access.svg?react";
import Analytics from "~/assets/images/icons/analytics.svg?react";
import Calendar from "~/assets/images/icons/calendar.svg?react";
import Check from "~/assets/images/icons/check.svg?react";
import Clipboard from "~/assets/images/icons/clipboard.svg?react";
import Contributors from "~/assets/images/icons/contributors.svg?react";
import Cross from "~/assets/images/icons/cross.svg?react";
Expand All @@ -27,6 +28,7 @@ const iconNameToSvg: Record<IconName, FC<React.SVGProps<SVGSVGElement>>> = {
access: Access,
analytics: Analytics,
calendar: Calendar,
check: Check,
clipboard: Clipboard,
contributors: Contributors,
cross: Cross,
Expand Down
Loading