Skip to content
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
8 changes: 5 additions & 3 deletions public/locales/en/badges.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
{
"nameColumn": "Name",
"descriptionColumn": "Description",
"imageColumn": "Image",
"nameLabel": "Name",
"namePlaceholder": "Enter badge name",
"descriptionLabel": "Description",
"descriptionPlaceholder": "Enter description",
"imageFieldLabel": "Image",
"title": "Badges area",
"addBadge": "Add badge",
"editBadge": "Edit badge",
Expand Down
3 changes: 2 additions & 1 deletion public/locales/en/namespaces.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"nameColumn": "Name",
"nameLabel": "Name",
"namePlaceholder":"Namespace name",
"title": "Namespaces area",
"addNameSpace": "Add namespace",
"editNameSpace": "Edit namespace",
Expand Down
8 changes: 5 additions & 3 deletions public/locales/pt/badges.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
{
"nameColumn": "Nome",
"descriptionColumn": "Descrição",
"imageColumn": "Imagem",
"nameLabel": "Nome",
"namePlaceholder": "Insira o nome da insígnia",
"descriptionLabel": "Descrição",
"descriptionPlaceholder": "Insira a descrição",
"imageFieldLabel": "Imagem",
"title": "Área de Insígnias",
"addBadge": "Adicionar Insígnia",
"editBadge": "Editar Insígnia",
Expand Down
2 changes: 1 addition & 1 deletion public/locales/pt/claimForm.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"checkboxAcceptTerms": "Eu atesto pessoalmente que toda informação adicionada é confiável.",
"cancelButton": "Cancelar",
"updateButton": "Atualizar",
"saveButton": "Adicionar",
"saveButton": "Salvar",
"contentModelTitle": "Formato",
"selectContentModel": "Que formato de afirmação você quer incluir?",
"Image": "Imagem",
Expand Down
3 changes: 2 additions & 1 deletion public/locales/pt/namespaces.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"nameColumn": "Nome",
"nameLabel": "Nome",
"namePlaceholder":"Nome do namespace",
"title": "Área de namespaces",
"addNameSpace": "Adicionar namespace",
"editNameSpace": "Editar namespace",
Expand Down
35 changes: 24 additions & 11 deletions server/auth/name-space/name-space.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,16 @@ import { Types } from "mongoose";
import { Roles } from "../../auth/ability/ability.factory";
import { NotificationService } from "../../notifications/notifications.service";
import slugify from "slugify";
import { ConfigService } from "@nestjs/config";

@Controller()
export class NameSpaceController {
constructor(
private nameSpaceService: NameSpaceService,
private usersService: UsersService,
private viewService: ViewService,
private notificationService: NotificationService
private notificationService: NotificationService,
private configService: ConfigService,
) {}

@AdminOnly()
Expand Down Expand Up @@ -59,13 +61,14 @@ export class NameSpaceController {
...namespaceBody,
};

newNameSpace.slug = slugify(nameSpace.name, {
newNameSpace.slug = slugify(newNameSpace.name, {
lower: true,
strict: true,
});

newNameSpace.users = await this.updateNameSpaceUsers(
newNameSpace.users,
newNameSpace.slug,
nameSpace.slug
);

Expand Down Expand Up @@ -100,25 +103,35 @@ export class NameSpaceController {
const users = await this.usersService.findAll({});
const parsedUrl = parse(req.url, true);

const query = Object.assign(parsedUrl.query, { nameSpaces, users });
const query = Object.assign(
parsedUrl.query,
{
sitekey: this.configService.get<string>("recaptcha_sitekey"),
nameSpaces,
users
}
);

await this.viewService.render(req, res, "/admin-namespaces", query);
}

private async updateNameSpaceUsers(users, key) {
private async updateNameSpaceUsers(users, newKey, oldKey = null) {
const promises = users.map(async (user) => {
const userId = Types.ObjectId(user._id);
const existingUser = await this.usersService.getById(userId);

if (!existingUser.role[key]) {
await this.usersService.updateUser(existingUser._id, {
role: {
...existingUser.role,
[key]: Roles.Regular,
},
});
let updatedRoles = { ...existingUser.role };

if (oldKey && oldKey !== newKey) {
delete updatedRoles[oldKey];
}

updatedRoles[newKey] = Roles.Regular;

await this.usersService.updateUser(existingUser._id, {
role: updatedRoles,
});

return userId;
});

Expand Down
5 changes: 4 additions & 1 deletion server/badge/badge.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { Types } from "mongoose";
import { ApiTags } from "@nestjs/swagger";
import { UtilService } from "../util";
import { AdminOnly } from "../auth/decorators/auth.decorator";
import { ConfigService } from "@nestjs/config";

@Controller(":namespace?")
export class BadgeController {
Expand All @@ -28,7 +29,8 @@ export class BadgeController {
private viewService: ViewService,
private imageService: ImageService,
private usersService: UsersService,
private util: UtilService
private util: UtilService,
private configService: ConfigService,
) {}

@AdminOnly()
Expand Down Expand Up @@ -152,6 +154,7 @@ export class BadgeController {
badges,
users,
nameSpace: req.params.namespace,
sitekey: this.configService.get<string>("recaptcha_sitekey"),
});

await this.viewService.render(req, res, "/admin-badges", query);
Expand Down
13 changes: 3 additions & 10 deletions server/verification-request/verification-request.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -704,15 +704,7 @@ export class VerificationRequestService {
return src._id;
})
);

updatedVerificationRequestData.source = Array.from(
new Set([
...(verificationRequest.source || []).map((sourceId) =>
sourceId.toString()
),
...newSourceIds.map((id) => id.toString()),
])
).map((id) => Types.ObjectId(id));
updatedVerificationRequestData.source = newSourceIds.map((id) => Types.ObjectId(id));
}

if (
Expand Down Expand Up @@ -743,7 +735,8 @@ export class VerificationRequestService {
verificationRequest._id,
updatedVerificationRequestData,
{ new: true, upsert: true }
).populate("source");
).populate("source")
.populate("impactArea");
} catch (error) {
this.logger.error("Failed to update verification request:", error);
throw error;
Expand Down
2 changes: 1 addition & 1 deletion src/components/Form/DatePickerInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const StyledTextField = styled(TextField)`

const DatePickerInput = (props) => {
const { t } = useTranslation();
const [value, setValue] = useState(null);
const [value, setValue] = useState(props.defaultValue || null);
const [open, setOpen] = useState(false);

return (
Expand Down
35 changes: 30 additions & 5 deletions src/components/Form/DynamicInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,26 @@ import { VisualEditorContext } from "../Collaborative/VisualEditorProvider";
import AletheiaInput from "../AletheiaInput";
import DatePickerInput from "./DatePickerInput";
import { Checkbox, FormControlLabel } from "@mui/material";
import ReportTypeSelect from "../VerificationRequest/CreateVerificationRequest/ReportTypeSelect";
import ImpactAreaSelect from "../VerificationRequest/CreateVerificationRequest/ImpactAreaSelect";
import colors from "../../styles/colors";
import ReportTypeSelect from "../VerificationRequest/verificationRequestForms/formInputs/ReportTypeSelect";
import ImpactAreaSelect from "../VerificationRequest/verificationRequestForms/formInputs/ImpactAreaSelect";
import InputExtraSourcesList from "../VerificationRequest/verificationRequestForms/formInputs/InputExtraSourcesList";
import { Topic } from "../../types/Topic";
import { SourceType } from "../../types/Source";
import ImageUpload, { UploadFile } from "../ImageUpload";

const VisualEditor = lazy(() => import("../Collaborative/VisualEditor"));

export type UnifiedDefaultValue = Topic | SourceType[] | UploadFile[] | string

interface DynamicInputProps {
fieldName: string;
type: string;
placeholder: string;
value: string | [];
value: UnifiedDefaultValue;
onChange: any;
addInputLabel: string;
defaultValue: string | [];
defaultValue: UnifiedDefaultValue;
"data-cy": string;
extraProps: any;
disabledDate?: any;
Expand Down Expand Up @@ -83,6 +89,15 @@ const DynamicInput = (props: DynamicInputProps) => {
white="true"
/>
);
case "sourceList":
return (
<InputExtraSourcesList
sources={props.value}
onChange={props.onChange}
disabled={props.disabled}
placeholder={props.placeholder}
/>
);
case "select":
return (
<ClaimReviewSelect
Expand All @@ -98,14 +113,23 @@ const DynamicInput = (props: DynamicInputProps) => {
defaultValue={props.defaultValue}
onChange={(value) => props.onChange(value)}
placeholder={t(props.placeholder)}

isDisabled={props.disabled}
/>
);
case "selectImpactArea":
return (
<ImpactAreaSelect
defaultValue={props.defaultValue}
onChange={(value) => props.onChange(value)}
placeholder={t(props.placeholder)}
isDisabled={props.disabled}
/>
);
case "imageUpload":
return (
<ImageUpload
onChange={(value) => props.onChange(value)}
defaultFileList={props.defaultValue}
/>
);
case "textbox":
Expand Down Expand Up @@ -141,6 +165,7 @@ const DynamicInput = (props: DynamicInputProps) => {
case "date":
return (
<DatePickerInput
defaultValue={props.defaultValue}
placeholder={t(props.placeholder)}
onChange={(value) => props.onChange(value)}
data-cy={"testSelectDate"}
Expand Down
14 changes: 11 additions & 3 deletions src/components/Form/FormField.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export type FormField = {
addInputLabel?: string;
defaultValue: string | [];
extraProps?: FormFieldExtraProps;
disabled?: boolean;
};

// Use to add properties specific to one type of field
Expand All @@ -38,6 +39,7 @@ interface CreateFormFieldProps extends Partial<FormField> {
i18nNamespace?: string;
required?: boolean;
isURLField?: boolean;
disabled?: boolean;
}

const createFormField = (props: CreateFormFieldProps): FormField => {
Expand All @@ -50,6 +52,7 @@ const createFormField = (props: CreateFormFieldProps): FormField => {
rules,
required = true,
isURLField = false,
disabled = false,
} = props;

return {
Expand All @@ -58,16 +61,17 @@ const createFormField = (props: CreateFormFieldProps): FormField => {
label: `${i18nNamespace}:${i18nKey}Label`,
placeholder: `${i18nNamespace}:${i18nKey}Placeholder`,
defaultValue,
disabled,
...props,
rules: {
required: required && "common:requiredFieldError",
required: !disabled && required && "common:requiredFieldError",
...rules,
validate: {
...(required && {
...(!disabled && required && {
notBlank: (v) =>
validateBlank(v) || "common:requiredFieldError",
}),
...(isURLField && {
...(!disabled && isURLField && {
validURL: (v) =>
!v ||
URL_PATTERN.test(v) ||
Expand Down Expand Up @@ -110,6 +114,10 @@ const fieldValidation = (value, validationFunction) => {
return dayjs(value).isValid() && dayjs(value).isBefore(dayjs());
}

if (Array.isArray(value) && value.length > 0 && (value[0].uid || value[0].originFileObj)) {
return true;
}

if (value instanceof Node) {
const editorParser = new EditorParser();
const schema = editorParser.editor2schema(value.toJSON());
Expand Down
21 changes: 17 additions & 4 deletions src/components/ImageUpload.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useRef, useState } from "react";
import React, { useEffect, useRef, useState } from "react";
import {
Grid,
Box,
Expand All @@ -11,6 +11,7 @@ import { FileUploadOutlined, DeleteOutline } from "@mui/icons-material";
import { useTranslation } from "next-i18next";
import AletheiaButton from "./Button";
import { MessageManager } from "../components/Messages";
import { UnifiedDefaultValue } from "./Form/DynamicInput";

export interface UploadFile {
uid: string;
Expand All @@ -24,18 +25,30 @@ export interface UploadFile {
interface ImageUploadProps {
onChange: (fileList: UploadFile[]) => void;
error?: boolean;
defaultFileList?: UploadFile[];
defaultFileList?: UnifiedDefaultValue;
}

const ImageUpload = ({
onChange,
error = false,
defaultFileList = [],
defaultFileList,
}: ImageUploadProps) => {
const { t } = useTranslation();
const fileInputRef = useRef<HTMLInputElement>(null);

const [fileList, setFileList] = useState<UploadFile[]>(defaultFileList);
const [fileList, setFileList] = useState<UploadFile[]>(() => {
if (Array.isArray(defaultFileList)) {
return (defaultFileList as UploadFile[]).filter(file => file.uid);
}
return [];
});

useEffect(() => {
if (Array.isArray(defaultFileList)) {
setFileList(defaultFileList as UploadFile[]);
}
}, [defaultFileList]);

const [previewOpen, setPreviewOpen] = useState(false);
const [previewImage, setPreviewImage] = useState("");
const [previewTitle, setPreviewTitle] = useState("");
Expand Down
Loading
Loading