-
Notifications
You must be signed in to change notification settings - Fork 66
Desafio CoreLab Todo App - Neil Lima #79
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
base: main
Are you sure you want to change the base?
Conversation
WalkthroughThis pull request introduces a complete full-stack implementation of the CoreLab Todo App. It adds extensive backend support using NestJS and MongoDB, as well as a modern React‑TypeScript frontend with multiple reusable components and style sheets. New CI/CD workflows, Docker configurations, and testing suites have been incorporated, along with comprehensive documentation updates and configuration improvements for linting, formatting, and type checking. Changes
Sequence Diagram(s)sequenceDiagram
participant C as Commit/Push
participant GH as GitHub Actions
participant FT as Frontend Test Job
participant BT as Backend Test Job
participant DB as Docker Build Job
C->>GH: Push to main / Pull Request
GH->>FT: Run Frontend Tests
GH->>BT: Run Backend Tests
FT-->>GH: Test Results
BT-->>GH: Test Results
alt Tests Passed
GH->>DB: Trigger Docker Build Job
else Tests Failed
GH->>C: Report Failure
end
sequenceDiagram
participant U as User
participant F as Frontend (React)
participant A as Axios API
participant N as Nginx
participant TC as TodoController
participant TS as TodoService
participant DB as MongoDB
U->>F: Interact with Todo UI
F->>A: Send API request (GET/POST/PUT/DELETE)
A->>N: Forward Request
N->>TC: Route to TodoController
TC->>TS: Invoke corresponding service
TS->>DB: Query/Update/Delete Todo
DB-->>TS: Return Data
TS-->>TC: Return Processed Data
TC-->>N: Send Response
N-->>A: Response from API
A-->>F: Return Data
F->>U: Update UI with response
Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (2)
✅ Files skipped from review due to trivial changes (1)
🚧 Files skipped from review as they are similar to previous changes (1)
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 36
🔭 Outside diff range comments (4)
er.name (1)
1-27
:⚠️ Potential issueRemove Git configuration file from repository
This appears to be a local Git configuration file containing personal user information that should not be committed to the repository. It contains:
- Duplicate LFS filter settings (lines 2-5 and 15-18)
- Personal user information (name and email)
- Machine-specific paths (
C:/Program Files/Git/...
)This file should be removed from version control as it:
- Contains personal information
- Overrides Git settings for other developers
- Includes machine-specific configurations
Consider adding a
.gitattributes
file instead if you need repository-specific Git configurations.frontend/src/pages/Vehicles/index.tsx (3)
30-43
: 🛠️ Refactor suggestionLoading state not reflected in the UI
The loading state is being tracked but not utilized in the UI. Consider adding a loading indicator when
loading
is true.return ( <div className={styles.Vehicles}> <main className={styles.main}> + {loading ? ( + <div className="loading">Loading vehicles...</div> + ) : ( <Search placeholder="Search" value={search} onChange={() => {}} /> <Button text="Add new vehicle" onClick={() => {}} /> <Card title="Sandero Stepway"> <p>Price: 22000</p> <p>Description: Carro usado por 2 anos...</p> <p>Year: 2018</p> </Card> + )} </main> </div> );
30-43
: 🛠️ Refactor suggestionFetched data not displayed in UI
The component fetches vehicle data but displays hardcoded content. Consider mapping through the
vehicles
array to dynamically render vehicle cards.return ( <div className={styles.Vehicles}> <main className={styles.main}> <Search placeholder="Search" value={search} onChange={() => {}} /> <Button text="Add new vehicle" onClick={() => {}} /> - <Card title="Sandero Stepway"> - <p>Price: 22000</p> - <p>Description: Carro usado por 2 anos...</p> - <p>Year: 2018</p> - </Card> + {vehicles.length > 0 ? ( + vehicles.map((vehicle) => ( + <Card key={vehicle.id} title={vehicle.name}> + <p>Price: {vehicle.price}</p> + <p>Description: {vehicle.description}</p> + <p>Year: {vehicle.year}</p> + </Card> + )) + ) : ( + <p>No vehicles found</p> + )} </main> </div> );
33-33
: 🛠️ Refactor suggestionEmpty onChange handler for Search component
The Search component has an empty onChange handler, making it non-functional. Implement the search functionality:
- <Search placeholder="Search" value={search} onChange={() => {}} /> + <Search + placeholder="Search" + value={search} + onChange={(e) => setSearch(e.target.value)} + />
🧹 Nitpick comments (93)
backend/package.json (1)
1-73
: Well-structured package.json with comprehensive configurationThe package.json contains all necessary configurations for a NestJS application with MongoDB integration, including appropriate scripts, dependencies, and test setup.
Consider these minor improvements:
- Add meaningful values for the empty
description
andauthor
fields (lines 4-5)- Consider updating Mongoose from v7.0.0 to the latest major version (v8.x) for potential performance improvements and new features
backend/README.md (4)
31-33
: Command Examples: Consideration for Shell PromptsThe command examples (lines 31–33) display a
$
prompt (e.g.,$ npm install
). While common in shell examples, some linting guidelines suggest removing the prompt for ease of copy-paste. If this is intentional for clarity, you may ignore this; otherwise, consider removing the$
to improve usability.🧰 Tools
🪛 markdownlint-cli2 (0.17.2)
32-32: Dollar signs used before commands without showing output
null(MD014, commands-show-output)
67-70
: Deployment Commands: Shell Prompt ConsistencySimilar to the installation command block, the deployment commands (lines 67–70) include a
$
prompt. Consider either removing the prompt for direct copy-paste compatibility or adding a note that the prompt is not part of the command.🧰 Tools
🪛 markdownlint-cli2 (0.17.2)
68-68: Dollar signs used before commands without showing output
null(MD014, commands-show-output)
69-69: Dollar signs used before commands without showing output
null(MD014, commands-show-output)
74-78
: Tone Adjustment: Replace Colloquial ExpressionIn the Resources section (line 76), the phrase “come in handy” is somewhat casual. Consider using a more formal alternative (e.g., “are useful” or “can be beneficial”) to maintain a professional tone.
🧰 Tools
🪛 LanguageTool
[style] ~76-~76: The expression “come in handy” is rather colloquial. Consider replacing it to add a more formal tone to your writing.
Context: ...ces Check out a few resources that may come in handy when working with NestJS: - Visit the ...(COME_IN_HANDY)
88-90
: Language Refinement: Adjust Adjectives in the Support SectionOn line 89, the phrase “the amazing backers” is used. To achieve a more formal tone, consider replacing “amazing” with a term like “valued” or “dedicated.” Additionally, review any compound adjective usage to ensure proper hyphenation if needed.
🧰 Tools
🪛 LanguageTool
[uncategorized] ~89-~89: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...). ## Support Nest is an MIT-licensed open source project. It can grow thanks to the spon...(EN_COMPOUND_ADJECTIVE_INTERNAL)
[style] ~89-~89: Consider using a more formal and expressive alternative to ‘amazing’.
Context: ...anks to the sponsors and support by the amazing backers. If you'd like to join them, pl...(AWESOME)
frontend/src/App.tsx (1)
1-1
: Consider fixing formatting issues instead of disabling ESLint.Rather than disabling the prettier/prettier rule, consider fixing the formatting issues to maintain consistent code style throughout the project.
frontend/src/utils/DeleteModalUtils.tsx (2)
1-1
: Consider fixing formatting issues instead of disabling ESLint.Rather than disabling the prettier/prettier rule, consider fixing the formatting issues to maintain consistent code style throughout the project.
2-6
: Consider the necessity of this abstraction layer.This utility function is a thin wrapper around the service function and doesn't add much functionality. Consider whether this abstraction layer provides enough value or if calling the service directly would be more straightforward.
If the wrapper exists to centralize error handling or provide additional business logic in the future, that would justify its existence.
backend/src/config/databaseConfig.ts (1)
1-1
: Consider fixing formatting issues instead of disabling ESLint.Rather than disabling the prettier/prettier rule, consider fixing the formatting issues to maintain consistent code style throughout the project.
frontend/src/services/DeleteModalServices.tsx (1)
1-1
: Consider addressing formatting issues instead of disabling ESLint rules.Rather than disabling the prettier/prettier rule, it would be better to fix the formatting issues that are causing the rule to trigger.
frontend/src/components/index.tsx (1)
1-1
: Consider addressing formatting issues instead of disabling ESLint rules.Rather than disabling the prettier/prettier rule globally, it would be better to fix the formatting issues that are causing the rule to trigger.
backend/src/config/corsConfig.ts (1)
1-1
: Consider addressing formatting issues instead of disabling ESLint rules.Rather than disabling the prettier/prettier rule, fix the formatting issues that are causing the rule to trigger.
frontend/src/types/TodoTypes.tsx (1)
1-8
: Consider using string for id when working with MongoDBThe interface looks well-structured for a todo item. However, I noticed you're using a number type for the
id
property. If you're working with MongoDB (as indicated in the PR description), consider using string instead since MongoDB uses string ObjectIds by default.export interface ITodo { - id: number; + id: string; title: string; description: string; isFavorite: boolean; color: string; createdAt: Date; }backend/src/app.module.ts (2)
1-1
: Remove ESLint disable commentUsing ESLint disable comments should be avoided when possible. Instead, fix the formatting issues or configure your ESLint rules appropriately.
-/* eslint-disable prettier/prettier */
12-13
: Add trailing newline at end of fileFiles should end with a newline character as per common coding standards. This helps prevent issues with certain tools and makes diffs cleaner.
export class AppModule {} +
Dockerfile.backend (3)
1-2
: Consider using a more specific Node versionUsing
node:16-alpine
is good for keeping the image size small, but consider pinning to a specific version (e.g.,node:16.20-alpine
) to ensure consistent builds.
9-10
: Consider using .dockerignoreTo optimize the build process, consider adding a
.dockerignore
file to exclude unnecessary files likenode_modules
,dist
, etc.
13-15
: Consider using a non-root user for better securityFor better security, consider adding a user and running the application as a non-root user.
EXPOSE 3333 +# Create a non-root user and switch to it +RUN addgroup -g 1001 -S nodejs && adduser -S nodejs -G nodejs +USER nodejs CMD ["npm", "run", "start:prod"]frontend/src/styles/theme.scss (2)
1-2
: Remove eslint-disable commentInstead of disabling the prettier/eslint rule, consider formatting the file according to the project's standards.
26-46
: Consider using CSS custom properties efficientlyYour light theme implementation is clean. For better maintenance, consider only overriding the variables that actually change between themes, rather than redefining all properties.
For example, the accent colors stay the same between themes, so they could be excluded from the light-mode override:
body.light-mode { --bg-primary: #f5f5f5; --bg-secondary: #ffffff; --bg-tertiary: #e0e0e0; --text-primary: #333333; --text-secondary: #666666; - --accent-primary: #4caf50; - --accent-secondary: #3d8b40; - --accent-tertiary: #2e7d32; - --error: #f44336; - --error-dark: #d32f2f; --border-color: #dddddd; // ...rest of the changes }frontend/src/utils/useDeleteModal.tsx (2)
1-1
: Remove the ESLint disable commentInstead of disabling ESLint rules globally for the file, it would be better to fix the underlying formatting issues that are triggering the warnings.
-/* eslint-disable prettier/prettier */
4-15
: LGTM! Well-structured custom hookThe
useDeleteModal
hook follows React best practices by clearly encapsulating modal state management and providing a clean API with descriptive function names.}; -}; +};Remove the trailing space at the end to avoid Prettier warnings.
frontend/src/types/DeleteModalTypes.tsx (1)
1-6
: Consider changing file extension and adding documentationThe interface is well-structured with appropriate property types, but there are a couple of improvements to consider.
Since this file only contains TypeScript types and no JSX, consider:
- Changing the file extension from
.tsx
to.ts
- Adding JSDoc comments for better documentation:
+/** + * Props for the DeleteModal component + */ export interface DeleteModalProps { + /** Whether the modal is currently visible */ isOpen: boolean; + /** The title text to display in the modal */ title: string; + /** Function called when user confirms deletion */ onConfirm: () => void; + /** Function called when user cancels deletion */ onCancel: () => void; -} +}backend/src/modules/todos/dto/update-todo.dto.ts (2)
1-1
: Remove the ESLint disable commentInstead of disabling ESLint rules globally for the file, it would be better to fix the underlying formatting issues that are triggering the warnings.
-/* eslint-disable prettier/prettier */
4-20
: Well-structured DTO with proper validationThe
UpdateTodoDto
class correctly implements partial updates with appropriate validation decorators. The use of@IsOptional()
allows for updating individual fields without requiring all properties.Consider adding JSDoc documentation for improved code readability:
+/** + * Data Transfer Object for updating Todo items + */ export class UpdateTodoDto { + /** Optional title for the Todo item */ @IsOptional() @IsString() title?: string; + /** Optional description for the Todo item */ @IsOptional() @IsString() description?: string; + /** Optional flag to mark Todo as favorite */ @IsOptional() @IsBoolean() isFavorite?: boolean; + /** Optional color code for the Todo item */ @IsOptional() @IsString() color?: string; -} +}backend/src/modules/todos/dto/create-todo.dto.ts (2)
1-1
: Remove the ESLint disable commentThis comment should be removed, and instead, the formatting issues should be fixed to comply with the project's linting rules.
-/* eslint-disable prettier/prettier */
17-19
: Consider adding more specific validation for the color propertyThe color property is currently only validated as a non-empty string. Consider adding pattern validation to ensure it follows a valid color format (like hex code).
@IsNotEmpty() @IsString() +@Matches(/^#[0-9A-Fa-f]{6}$/, { message: 'Color must be a valid hex color code' }) color: string;
backend/src/modules/todos/module/todo.module.ts (1)
1-1
: Remove the ESLint disable commentThis comment should be removed, and instead, the formatting issues should be fixed to comply with the project's linting rules.
-/* eslint-disable prettier/prettier */
backend/src/modules/todos/interfaces/todo.interface.ts (3)
1-1
: Remove unnecessary ESLint disable comment.The ESLint disable comment for prettier/prettier should be removed unless there's a specific formatting issue that can't be resolved.
-/* eslint-disable prettier/prettier */
4-10
: Consider adding JSDoc comments to improve documentation.The Todo interface is well-structured, but adding JSDoc comments would improve documentation and provide more context about each property's purpose.
+/** + * Interface representing a Todo item + */ export interface Todo { + /** Title of the todo item */ title: string; + /** Detailed description of the todo item */ description: string; + /** Flag indicating if the todo is marked as favorite */ isFavorite: boolean; + /** Color code for the todo item */ color: string; + /** Creation timestamp of the todo item */ createdAt: Date; }
12-12
: Add JSDoc comment to explain the purpose of the TodoDocument interface.The empty interface extension pattern is common in Mongoose, but a comment would clarify its purpose for other developers.
+/** + * Interface that extends the Todo interface with Mongoose Document properties + * for database operations and document methods + */ export interface TodoDocument extends Todo, Document {}frontend/src/components/Card/index.tsx (1)
5-5
: Use destructuring for props to improve readability.Destructuring the props in the function parameters would make the component more readable and reduce the need for
props.
prefix in the JSX.-const Card = (props: ICardProps) => { +const Card: React.FC<ICardProps> = ({ title, children }) => {Also, update the JSX to use the destructured props:
return ( <div className={styles.Card}> - <h2>{props.title}</h2> + <h2>{title}</h2> <div className={styles.content}> - {props.children} + {children} </div> </div> );frontend/src/components/SearchBar/index.tsx (2)
1-1
: Remove unnecessary ESLint disable comment.The ESLint disable comment for prettier/prettier should be removed unless there's a specific formatting issue that can't be resolved.
-/* eslint-disable prettier/prettier */
5-9
: Consider moving the interface to a dedicated types file.The Card component imports its props interface from a central types file, but the SearchBar defines its interface locally. Consider moving this interface to a dedicated types file for consistency.
-interface SearchBarProps { - value: string; - onChange: (value: string) => void; - placeholder?: string; -}Create a new file at
src/types/SearchBarTypes.tsx
:export interface SearchBarProps { value: string; onChange: (value: string) => void; placeholder?: string; }Then import it in this file:
import React from 'react'; import '../../styles/SearchBarStyles.scss'; +import { SearchBarProps } from '../../types/SearchBarTypes';
frontend/src/services/TodoCardServices.tsx (1)
1-10
: Well-structured service functions with proper typing.The TodoCardServices implementation provides clean, focused functions for toggling favorites and deleting todos. The return type Promise properly indicates that toggleFavorite returns the updated todo object, while Promise correctly shows that deleteTodo doesn't return data.
Consider adding error handling to these functions to gracefully manage API failures rather than letting errors propagate to components.
Here's how you could improve error handling:
export const toggleFavorite = async (id: number, isFavorite: boolean): Promise<ITodo> => { - return put(`/todos/${id}`, { isFavorite }); + try { + return await put(`/todos/${id}`, { isFavorite }); + } catch (error) { + console.error(`Failed to toggle favorite for todo ${id}:`, error); + throw error; + } }; export const deleteTodo = async (id: number): Promise<void> => { - return del(`/todos/${id}`); + try { + return await del(`/todos/${id}`); + } catch (error) { + console.error(`Failed to delete todo ${id}:`, error); + throw error; + } };frontend/src/services/TodoFormServices.tsx (1)
1-10
: Well-implemented service functions with proper TypeScript usage.Good use of TypeScript features:
Omit<ITodo, 'id' | 'createdAt'>
correctly ensures that the client doesn't provide server-generated fields when creating todosPartial<ITodo>
appropriately allows updating only specific fields of a todo itemAs with the TodoCardServices, consider adding error handling to gracefully manage API failures.
export const createTodo = async (todo: Omit<ITodo, 'id' | 'createdAt'>): Promise<ITodo> => { - return post('/todos', todo); + try { + return await post('/todos', todo); + } catch (error) { + console.error('Failed to create todo:', error); + throw error; + } }; export const updateTodo = async (id: number, todo: Partial<ITodo>): Promise<ITodo> => { - return put(`/todos/${id}`, todo); + try { + return await put(`/todos/${id}`, todo); + } catch (error) { + console.error(`Failed to update todo ${id}:`, error); + throw error; + } };backend/src/main.ts (2)
1-1
: Remove unnecessary ESLint disable comment.This ESLint disable directive suggests there are formatting issues that should be fixed rather than suppressed.
-/* eslint-disable prettier/prettier */
7-20
: Good NestJS setup, but consider environment configuration.The bootstrap function correctly sets up the NestJS application with proper validation pipes and CORS configuration. However, hardcoding the port (3333) limits flexibility across different environments.
Consider using environment variables for configuration:
async function bootstrap() { const app = await NestFactory.create(AppModule); // Configuração de validação global app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true, })); // Configuração de CORS app.enableCors(corsConfig); - await app.listen(3333); + const port = process.env.PORT || 3333; + await app.listen(port); + console.log(`Application is running on: http://localhost:${port}`); }Additionally, consider adding error handling:
async function bootstrap() { - const app = await NestFactory.create(AppModule); - - // Configuração de validação global - app.useGlobalPipes(new ValidationPipe({ - whitelist: true, - transform: true, - })); - - // Configuração de CORS - app.enableCors(corsConfig); - - await app.listen(3333); + try { + const app = await NestFactory.create(AppModule); + + // Configuração de validação global + app.useGlobalPipes(new ValidationPipe({ + whitelist: true, + transform: true, + })); + + // Configuração de CORS + app.enableCors(corsConfig); + + const port = process.env.PORT || 3333; + await app.listen(port); + console.log(`Application is running on: http://localhost:${port}`); + } catch (error) { + console.error('Failed to start application:', error); + process.exit(1); + } }backend/src/modules/todos/schemas/todo.schema.ts (3)
1-1
: Remove the eslint-disable comment or address the underlying formatting issues.Instead of disabling the Prettier rules globally for the file, fix the underlying formatting issues or configure Prettier settings properly in the project.
-/* eslint-disable prettier/prettier */
18-19
: Consider adding color validation.The color field is required but doesn't have any validation for the format or allowed values. Consider adding validation to ensure it follows a valid format (e.g., hex code, named color).
@Prop({ required: true }) - color: string; + @Prop({ + required: true, + validate: { + validator: (v: string) => /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(v), + message: props => `${props.value} is not a valid hex color!` + } + }) + color: string;
25-25
: Remove trailing whitespace at end of line.There's a trailing space at the end of line 25 after "Todo;".
-export const TodoSchema = SchemaFactory.createForClass(Todo); +export const TodoSchema = SchemaFactory.createForClass(Todo);Dockerfile.frontend (2)
13-19
: Consider adding security improvements to the Nginx container.The Nginx container configuration could benefit from security enhancements like running as a non-root user and adding a health check.
FROM nginx:alpine COPY --from=build /app/build /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf +# Create a non-root user +RUN addgroup -g 1001 -S app && \ + adduser -u 1001 -S app -G app && \ + chown -R app:app /usr/share/nginx/html + +# Switch to non-root user +USER app + +# Add healthcheck +HEALTHCHECK --interval=30s --timeout=3s CMD wget --quiet --tries=1 --spider http://localhost:80/ || exit 1 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]
21-21
: Remove trailing whitespace at end of line.There's a trailing space at the end of line 21.
-CMD ["nginx", "-g", "daemon off;"] +CMD ["nginx", "-g", "daemon off;"]frontend/src/components/DeleteModal/index.tsx (3)
1-1
: Remove the eslint-disable comment.Instead of disabling the Prettier rules for the entire file, fix the underlying formatting issues or configure Prettier settings properly in the project.
-/* eslint-disable prettier/prettier */
12-13
: Consider implementing internationalization for text content.The modal text is hardcoded in Portuguese. Consider implementing an internationalization solution for better maintainability and language support.
27-27
: Remove trailing whitespace at end of line.There's a trailing space at the end of line 27.
-export default DeleteModal; +export default DeleteModal;frontend/src/pages/Vehicles/index.tsx (1)
28-28
: Remove console.log statementThis console.log statement should be removed before production deployment.
- console.log({ vehicles });
docker-compose.yml (3)
14-27
: Add health checks for backend serviceConsider adding health checks to ensure the service is fully initialized before dependent services try to connect:
backend: build: context: . dockerfile: Dockerfile.backend container_name: backend ports: - "3333:3333" environment: - NODE_ENV=production - MONGODB_URI=mongodb://mongodb:27017/todolist_db depends_on: - mongodb + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3333/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s networks: - app-network
14-27
: Add restart policy for production resilienceFor production environments, it's important to define restart policies to ensure services recover from failures:
backend: build: context: . dockerfile: Dockerfile.backend container_name: backend + restart: unless-stopped ports: - "3333:3333"
46-46
: Fix file endingThe file has trailing spaces and no newline at the end. This should be fixed:
- volumes: - mongodb_data: + volumes: + mongodb_data:🧰 Tools
🪛 YAMLlint (1.35.1)
[error] 46-46: no new line character at the end of file
(new-line-at-end-of-file)
[error] 46-46: trailing spaces
(trailing-spaces)
frontend/src/styles/global.scss (2)
1-1
: Consider removing ESLint disable commentInstead of disabling Prettier formatting for the entire file, consider configuring Prettier to handle SCSS files correctly.
59-59
: Add newline at end of fileAdd a newline at the end of the file to comply with standard coding practices:
.modal { animation: fadeIn 0.3s ease-out; -} +} +frontend/src/services/TodoServices.tsx (2)
1-16
: Consider caching strategy and error handling improvementsThe React Query implementation follows best practices by using query keys and separating the data fetching logic from the hook. However:
- Setting both
staleTime
andgcTime
to 0 might lead to unnecessary network requests - consider if some minimal caching would improve performance- There's no error handling in the
getTodos
functionexport const getTodos = async (): Promise<ITodo[]> => { - return get('/todos'); + try { + return await get('/todos'); + } catch (error) { + console.error('Failed to fetch todos:', error); + throw error; + } }; export const useTodos = () => { return useQuery<ITodo[], Error>({ queryKey: ['todos'], queryFn: getTodos, - staleTime: 0, // Sempre considerar os dados como obsoletos - gcTime: 0 // Não manter em cache + staleTime: 30000, // Consider data stale after 30 seconds + gcTime: 300000 // Keep unused data in cache for 5 minutes }); };
18-20
: Improve type safety for queryClient parameterThe
queryClient
parameter is typed asany
, which reduces type safety. Consider using the proper type from React Query.-export const invalidateTodosCache = (queryClient: any) => { +import { QueryClient } from '@tanstack/react-query'; + +export const invalidateTodosCache = (queryClient: QueryClient) => { queryClient.invalidateQueries(['todos']); };frontend/src/lib/api.ts (1)
9-9
: Remove unused endpoint functionThe
endpoint
function is defined but not used anywhere in the file.-const endpoint = (path: string): string => API + path;
frontend/src/utils/TodoCardUtils.tsx (1)
12-18
: Avoid redundant Date object creationCreating a new Date from an existing Date object is unnecessary and potentially introduces bugs if the input isn't actually a Date object.
export const formatDate = (date: Date): string => { - return new Date(date).toLocaleDateString('pt-BR', { + return date.toLocaleDateString('pt-BR', { day: 'numeric', month: 'short', year: 'numeric' }); };frontend/src/components/DeleteModal/DeleteModalStyles.scss (1)
33-64
: Consider using SCSS variables for colors.While the button styling is well-implemented with appropriate visual distinction and hover effects, consider using SCSS variables for colors to improve maintainability and consistency throughout the application.
+ // At the top of the file or in a separate variables file + $cancel-bg: #e0e0e0; + $cancel-hover: #d0d0d0; + $confirm-bg: #dc3545; + $confirm-hover: #c82333; + $text-dark: #333; + $text-medium: #666; &.cancel-button { - background-color: #e0e0e0; - color: #333; + background-color: $cancel-bg; + color: $text-dark; &:hover { - background-color: #d0d0d0; + background-color: $cancel-hover; } } &.confirm-button { - background-color: #dc3545; + background-color: $confirm-bg; color: white; &:hover { - background-color: #c82333; + background-color: $confirm-hover; } }backend/src/config/id-formatter.ts (3)
1-3
: Remove unnecessary ESLint disable comment.Instead of disabling ESLint rules, consider fixing the underlying formatting issues to maintain code quality standards.
- /* eslint-disable prettier/prettier */ import { Types } from 'mongoose';
32-37
: Remove ESLint disable comment in toNumber method.Rather than disabling the ESLint rule, rename the parameter to remove the underscore since it is being used.
- // eslint-disable-next-line @typescript-eslint/no-unused-vars - static toNumber(_id: Types.ObjectId): number { + static toNumber(id: Types.ObjectId): number { // Use the ObjectId timestamp to generate a unique ID // Usar o timestamp do ObjectId para gerar um ID único - const timestamp = _id.getTimestamp().getTime(); + const timestamp = id.getTimestamp().getTime(); return timestamp; }
4-38
: Consider using standalone functions instead of a static-only class.As noted by static analysis, classes with only static methods can be replaced with standalone functions, which is generally considered better practice in TypeScript/JavaScript.
- export class IdFormatter { - static toObjectId(id: string | number): Types.ObjectId { + export function toObjectId(id: string | number): Types.ObjectId { // Implementation... } - static toString(id: Types.ObjectId): string { + export function toString(id: Types.ObjectId): string { return id.toString(); } - static toNumber(_id: Types.ObjectId): number { + export function toNumber(id: Types.ObjectId): number { // Implementation... } - }🧰 Tools
🪛 Biome (1.9.4)
[error] 4-38: Avoid classes that contain only static members.
Prefer using simple functions instead of classes with only static members.
(lint/complexity/noStaticOnlyClass)
frontend/src/styles/TodoFormStyles.scss (2)
1-1
: Remove ESLint disable comment.The ESLint disable comment for prettier at the top of the file should be removed. Instead, configure your project's ESLint and Prettier settings properly to work together without needing to disable rules.
-/* eslint-disable prettier/prettier */
5-13
: Extract color values as SCSS variables.The colors defined in the
COLORS
array in TodoFormUtils.tsx should be consistent with these styles. Consider extracting these color values as SCSS variables in your theme file to ensure consistency across your application.+ // In theme.scss + $color-red: #f44336; + $color-orange: #ff9800; + $color-yellow: #ffeb3b; + $color-green: #4caf50; + $color-blue: #2196f3; + $color-purple: #9c27b0; + $color-brown: #795548;frontend/src/index.tsx (1)
1-1
: Remove ESLint disable comment.The ESLint disable comment for prettier at the top of the file should be removed. Instead, configure your project's ESLint and Prettier settings properly to work together.
-/* eslint-disable prettier/prettier */
frontend/src/components/TodoForm/index.tsx (2)
43-55
: Improve color selector accessibility.While the color selector looks good visually, it lacks proper accessibility attributes. Users with screen readers might have difficulty understanding which color is selected.
<div key={c} className={`color-option ${color === c ? 'color-option--selected' : ''}`} style={{ backgroundColor: c }} onClick={() => setColor(c)} + aria-label={`Color ${COLORS.indexOf(c) + 1}`} + role="radio" + aria-checked={color === c} />Also consider adding a tooltip or some other indication of what each color represents.
57-66
: Improve checkbox accessibility and styling.The checkbox for marking a task as favorite could benefit from improved styling and accessibility.
<div className="form-group"> - <label> + <label htmlFor="favorite" className="checkbox-label"> <input + id="favorite" type="checkbox" checked={isFavorite} onChange={(e) => setIsFavorite(e.target.checked)} + aria-label="Mark as favorite" /> {' '}Marcar como favorito </label> </div>frontend/src/utils/TodoFormUtils.tsx (2)
30-44
: Add form reset after submission.The form currently doesn't reset after submission. Consider adding a reset function to clear the form fields after a successful submission for better user experience, especially when creating multiple todos.
const handleSubmit = (e: React.FormEvent, onSave: (todo: Omit<ITodo, 'id' | 'createdAt'>) => void) => { e.preventDefault(); if (!title.trim()) { alert('O título é obrigatório!'); return; } onSave({ title, description, color, isFavorite, }); + + // Reset form if not editing (no todo provided) + if (!todo) { + setTitle(''); + setDescription(''); + setColor(COLORS[0]); + setIsFavorite(false); + } };
5-13
: Consider making COLORS enum or object for better typing.The COLORS array is well-defined, but consider using an enum or an object with named color values for better type safety and readability in the codebase.
-export const COLORS = [ - '#f44336', // Vermelho - '#ff9800', // Laranja - '#ffeb3b', // Amarelo - '#4caf50', // Verde - '#2196f3', // Azul - '#9c27b0', // Roxo - '#795548', // Marrom -]; +export enum TodoColor { + RED = '#f44336', + ORANGE = '#ff9800', + YELLOW = '#ffeb3b', + GREEN = '#4caf50', + BLUE = '#2196f3', + PURPLE = '#9c27b0', + BROWN = '#795548' +} + +export const COLORS = Object.values(TodoColor);This approach would allow you to use
TodoColor.RED
in your code for better readability and type checking.backend/test/todo.service.spec.ts (1)
66-68
: Improve test assertion specificityThe current test verifies the length is greater than 0, but a more precise assertion would check for the exact expected output.
const result = await service.findAll(); - expect(result.length).toBeGreaterThan(0); + expect(result).toEqual(mockTodos); expect(mockTodoModel.find).toHaveBeenCalled();frontend/src/components/TodoCard/index.tsx (3)
1-1
: Avoid global ESLint disablingIt's better to address specific linting issues rather than disabling prettier entirely. Consider fixing the specific formatting issues.
- /* eslint-disable prettier/prettier */ + // Fix specific linting issues instead of disabling prettier
37-39
: Enhance accessibility for favorite buttonUsing star symbols (★/☆) may not be sufficient for screen readers. Consider adding a more descriptive visual indicator along with the aria-label.
<button className={`favorite-button ${isFavorite ? 'is-favorite' : ''}`} onClick={() => onToggleFavorite(id, !isFavorite)} aria-label={isFavorite ? 'Remover dos favoritos' : 'Adicionar aos favoritos'} > - {isFavorite ? '★' : '☆'} + <span className="sr-only">{isFavorite ? 'Favorito' : 'Não favorito'}</span> + <span aria-hidden="true">{isFavorite ? '★' : '☆'}</span> </button>
57-65
: Enhance accessibility for action buttonsReplace emoji characters with more accessible icon solutions for edit and delete buttons.
<button className="edit-button" onClick={() => onEdit(todo)} aria-label="Editar tarefa" > - ✏️ + <span aria-hidden="true" className="edit-icon">✏️</span> + <span className="sr-only">Editar</span> </button> <button className="delete-button" onClick={openModal} aria-label="Excluir tarefa" > - 🗑️ + <span aria-hidden="true" className="delete-icon">🗑️</span> + <span className="sr-only">Excluir</span> </button>frontend/src/components/TodoFilters/index.tsx (1)
25-41
: Improve color filter accessibilityThe color options may not be accessible to users with color blindness. Consider adding patterns or labels to distinguish colors beyond just visual differences.
<div className="color-filters"> <div className={`color-option ${selectedColor === null ? 'color-option--selected' : ''}`} style={{ background: 'linear-gradient(to right, red, orange, yellow, green, blue, indigo, violet)' }} onClick={() => onColorSelect(null)} title="Todas as cores" + role="button" + tabIndex={0} + aria-pressed={selectedColor === null} /> {COLORS.map((color) => ( <div key={color} className={`color-option ${selectedColor === color ? 'color-option--selected' : ''}`} style={{ backgroundColor: color }} onClick={() => onColorSelect(color)} title={color} + role="button" + tabIndex={0} + aria-pressed={selectedColor === color} /> ))} </div>.github/workflows/ci.yml (2)
72-75
: Add more descriptive job nameThe job name 'docker-build' could be more descriptive to indicate it's a deployment step.
- docker-build: + deploy-docker-images: needs: [frontend-test, backend-test] if: github.ref == 'refs/heads/main' runs-on: ubuntu-latest
1-97
: Clean up trailing spaces and add newline at end of fileThe YAML file contains numerous trailing spaces and is missing a newline at the end, which could cause issues with some tools.
Use an editor configuration or linting tool like EditorConfig or Prettier to automatically clean up trailing spaces and ensure files end with a newline.
🧰 Tools
🪛 actionlint (1.7.4)
14-14: the runner of "actions/checkout@v2" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
17-17: the runner of "actions/setup-node@v2" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
45-45: the runner of "actions/checkout@v2" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
48-48: the runner of "actions/setup-node@v2" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
78-78: the runner of "actions/checkout@v2" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
81-81: the runner of "docker/setup-buildx-action@v1" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
84-84: the runner of "docker/build-push-action@v2" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
92-92: the runner of "docker/build-push-action@v2" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
🪛 YAMLlint (1.35.1)
[error] 15-15: trailing spaces
(trailing-spaces)
[error] 20-20: trailing spaces
(trailing-spaces)
[error] 25-25: trailing spaces
(trailing-spaces)
[error] 30-30: trailing spaces
(trailing-spaces)
[error] 35-35: trailing spaces
(trailing-spaces)
[error] 46-46: trailing spaces
(trailing-spaces)
[error] 51-51: trailing spaces
(trailing-spaces)
[error] 56-56: trailing spaces
(trailing-spaces)
[error] 61-61: trailing spaces
(trailing-spaces)
[error] 66-66: trailing spaces
(trailing-spaces)
[error] 79-79: trailing spaces
(trailing-spaces)
[error] 82-82: trailing spaces
(trailing-spaces)
[error] 90-90: trailing spaces
(trailing-spaces)
[error] 97-97: no new line character at the end of file
(new-line-at-end-of-file)
[error] 97-97: trailing spaces
(trailing-spaces)
frontend/src/styles/TodoFiltersStyles.scss (2)
1-1
: Consider removing eslint-disable commentThis eslint-disable comment for Prettier might not be necessary if you configure your ESLint and Prettier properly to work together. Consider removing it or addressing the underlying formatting issues.
-/* eslint-disable prettier/prettier */
38-56
: Consider adding keyboard accessibility for color optionsThe color options are only accessible via mouse clicks. Consider enhancing accessibility by adding keyboard navigation support.
.color-option { width: 30px; height: 30px; border-radius: 50%; cursor: pointer; transition: all 0.3s; border: 2px solid transparent; box-shadow: 0 2px 5px var(--shadow-color); + outline: none; &:hover { transform: scale(1.1); box-shadow: 0 4px 10px var(--shadow-color); } + &:focus-visible { + border-color: var(--text-primary); + box-shadow: 0 0 0 2px var(--accent-primary), 0 4px 10px var(--shadow-color); + } &--selected { border-color: var(--text-primary); box-shadow: 0 0 0 2px var(--accent-primary), 0 4px 10px var(--shadow-color); } }frontend/src/components/Todo/index.tsx (4)
1-1
: Remove eslint-disable commentRather than disabling Prettier, consider configuring your ESLint and Prettier to work together properly.
-/* eslint-disable prettier/prettier */
28-33
: Use combined state updateInstead of maintaining separate
todos
andfilteredTodos
states that need to be synchronized, consider derivingfilteredTodos
directly fromtodosData
in the filtering effect.// Atualizar o estado local quando os dados do React Query forem carregados -useEffect(() => { - if (todosData) { - setTodos(todosData); - } -}, [todosData]);Then modify the filtering effect:
// Aplicar filtros useEffect(() => { - let result = [...todos]; + if (!todosData) return; + let result = [...todosData]; // Rest of the filtering logic... setFilteredTodos(result); -}, [todos, selectedColor, showOnlyFavorites, searchQuery]); +}, [todosData, selectedColor, showOnlyFavorites, searchQuery]);
104-143
: Extract reusable cache update logicThe cache update logic is duplicated between creating and updating todos. Consider extracting this to a reusable function.
+const updateQueryCache = (updatedTodo: ITodo, isNew = false) => { + // Update the React Query cache + queryClient.setQueryData(['todos'], (oldData: ITodo[] | undefined) => { + if (!oldData) return isNew ? [updatedTodo] : []; + return isNew + ? [...oldData, updatedTodo] + : oldData.map(todo => todo.id === updatedTodo.id ? updatedTodo : todo); + }); + + // Invalidate the cache to force a new fetch + invalidateTodosCache(queryClient); + + // Update the local state + setTodos(prev => isNew + ? [...prev, updatedTodo] + : prev.map(todo => todo.id === updatedTodo.id ? updatedTodo : todo) + ); +}; const handleSave = async (todoData: Omit<ITodo, 'id' | 'createdAt'>) => { try { if (currentTodo) { // Update existing todo const updatedTodo = await TodoFormUtils.updateExistingTodo(currentTodo.id, todoData); - - // Update the React Query cache - queryClient.setQueryData(['todos'], (oldData: ITodo[] | undefined) => { - if (!oldData) return []; - return oldData.map(todo => todo.id === currentTodo.id ? updatedTodo : todo); - }); - - // Invalidate the cache to force a new fetch - invalidateTodosCache(queryClient); - - // Update the local state - setTodos(prev => prev.map(todo => todo.id === currentTodo.id ? updatedTodo : todo)); + updateQueryCache(updatedTodo); } else { // Create new todo const newTodo = await TodoFormUtils.createNewTodo(todoData); - - // Update the React Query cache - queryClient.setQueryData(['todos'], (oldData: ITodo[] | undefined) => { - if (!oldData) return [newTodo]; - return [...oldData, newTodo]; - }); - - // Invalidate the cache to force a new fetch - invalidateTodosCache(queryClient); - - // Update the local state - setTodos(prev => [...prev, newTodo]); + updateQueryCache(newTodo, true); } setIsModalOpen(false); setCurrentTodo(undefined); } catch (error) { console.error('Erro ao salvar tarefa:', error); + // Add user-facing error message } };
203-205
: Use i18n for text messagesConsider using an internationalization library instead of hardcoding Portuguese text to make it easier to support multiple languages consistently.
<p style={{ color: 'var(--text-primary)', textAlign: 'center' }}> - Nenhuma tarefa encontrada. + {t('no_tasks_found', 'No tasks found.')} </p>frontend/src/styles/SearchBarStyles.scss (2)
1-1
: Remove eslint-disable commentRather than disabling Prettier, configure your ESLint and Prettier to work together properly.
-/* eslint-disable prettier/prettier */
4-38
: Remove duplicate search component stylesThere are two similar search components with different styles (
.search-bar
and.search-container
). Consider consolidating them into a single reusable component with variants if needed.Also applies to: 40-87
frontend/src/styles/TodoStyles.scss (1)
1-1
: Remove eslint-disable commentRather than disabling Prettier, configure your ESLint and Prettier to work together properly.
-/* eslint-disable prettier/prettier */
README.md (2)
49-51
: Add language specifiers to code blocks for better syntax highlighting and readability.The Markdown code blocks throughout the document are missing language specifiers, which would enable syntax highlighting. This is particularly important for command blocks.
Update the code blocks by adding language specifiers like this:
-``` +```bash cd backendAlso applies to: 54-56, 61-63, 70-72, 75-77, 80-82, 101-103, 109-117
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)
49-49: Fenced code blocks should have a language specified
null(MD040, fenced-code-language)
65-65
: Fix URL formatting in backticks.The URLs within backticks contain syntax that may cause rendering issues according to RFC 1738.
To ensure proper rendering, consider one of these alternatives:
-O servidor estará disponível em `http://localhost:3333`. +O servidor estará disponível em http://localhost:3333.Or use inline code without backticks:
-O servidor estará disponível em `http://localhost:3333`. +O servidor estará disponível em: http://localhost:3333Also applies to: 84-84
🧰 Tools
🪛 LanguageTool
[inconsistency] ~65-~65: O URL contém o caratére inválido
segundo RFC 1738. Os caratéres especiais podem ser codificados com % seguido de dois números hexadecimais. Context: ...ev ``` O servidor estará disponível em
http://localhost:3333`. ### Frontend 1. Navegue até a pasta d...(URL_VALIDATION)
backend/src/modules/todos/controllers/todo.controller.ts (2)
1-1
: Consider removing the eslint-disable comment.The
eslint-disable prettier/prettier
comment at the top of the file disables prettier formatting checks for the entire file, which is generally not recommended for maintainability.If there are specific formatting issues, consider addressing them directly rather than disabling prettier for the entire file.
-/* eslint-disable prettier/prettier */
27-30
: Add validation for the ID parameter in update and remove methods.The
id
parameter in theupdate
andremove
methods is not validated, which could lead to issues if invalid IDs are provided.Consider using a validation pipe for the ID parameter:
+ import { Controller, Get, Post, Body, Put, Param, Delete, ParseMongoIdPipe } from '@nestjs/common'; @Put(':id') - update(@Param('id') id: string, @Body() updateTodoDto: UpdateTodoDto): Promise<Todo> { + update(@Param('id', ParseMongoIdPipe) id: string, @Body() updateTodoDto: UpdateTodoDto): Promise<Todo> { return this.todoService.update(id, updateTodoDto); } @Delete(':id') - remove(@Param('id') id: string | number): Promise<void> { + remove(@Param('id', ParseMongoIdPipe) id: string): Promise<void> { return this.todoService.remove(id); }This assumes you have a
ParseMongoIdPipe
available or you can useParseUUIDPipe
if you're using UUIDs.Also applies to: 32-35
frontend/src/styles/TodoCardStyles.scss (2)
1-1
: Consider removing the eslint-disable comment.The
eslint-disable prettier/prettier
comment at the top of the file disables prettier formatting checks for the entire file, which is generally not recommended for maintainability.If there are specific formatting issues, consider addressing them directly rather than disabling prettier for the entire file.
-/* eslint-disable prettier/prettier */
8-8
: Consider refactoring the shadow properties into variables.The box-shadow definitions appear in multiple places but might change with different themes.
Extract the box-shadow values into CSS variables in your theme.scss file to maintain consistency and simplify theme switching:
- box-shadow: var(--card-shadow); + box-shadow: var(--card-shadow); - box-shadow: 0 8px 25px var(--shadow-color); + box-shadow: var(--card-shadow-hover);Then define these variables in your theme.scss file:
:root { --card-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); --card-shadow-hover: 0 8px 25px rgba(0, 0, 0, 0.15); } [data-theme="dark"] { --card-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); --card-shadow-hover: 0 8px 25px rgba(0, 0, 0, 0.3); }Also applies to: 16-16
PULL_REQUEST.md (1)
139-139
: Consider replacing the Anglicism "performance".In Portuguese, it's preferable to use "desempenho" instead of "performance" which is considered an Anglicism.
-O projeto foi desenvolvido seguindo todas as especificações do desafio, com foco em qualidade de código, usabilidade e performance. +O projeto foi desenvolvido seguindo todas as especificações do desafio, com foco em qualidade de código, usabilidade e desempenho.🧰 Tools
🪛 LanguageTool
[locale-violation] ~139-~139: “performance” é um estrangeirismo. É preferível dizer “desempenho”, “atuação”, “apresentação”, “espetáculo” ou “interpretação”.
Context: ...o em qualidade de código, usabilidade e performance. Adicionei vários extras para demonstra...(PT_BARBARISMS_REPLACE_PERFORMANCE)
backend/src/modules/todos/services/todo.service.ts (3)
1-1
: Consider removing or justifying the ESLint/Prettier override.If there's no specific formatting conflict, it might be beneficial to remove the
/* eslint-disable prettier/prettier */
comment to ensure consistent code formatting across the project.
16-25
: Handle potential save errors more gracefully.The
create
method correctly creates and saves a new Todo document. However, wrapping the save in atry/catch
block could provide a more user-friendly error message if the database operation fails.async create(createTodoDto: CreateTodoDto): Promise<Todo> { - const createdTodo = new this.todoModel(createTodoDto); - const savedTodo = await createdTodo.save(); + let savedTodo; + try { + const createdTodo = new this.todoModel(createTodoDto); + savedTodo = await createdTodo.save(); + } catch (error) { + // You could throw a custom exception or log the error + throw new Error('Failed to save the todo item. ' + error.message); + } const todoObj = savedTodo.toObject(); todoObj.id = IdFormatter.toNumber(savedTodo._id); return todoObj; }
27-36
: Use.lean()
for better performance when finding all documents.Calling
.lean()
on the Mongoose query will return plain JavaScript objects instead of Mongoose documents, resulting in less overhead. This can provide a performance boost for large datasets.- const todos = await this.todoModel.find().sort({ isFavorite: -1, createdAt: -1 }).exec(); + const todos = await this.todoModel + .find() + .sort({ isFavorite: -1, createdAt: -1 }) + .lean() + .exec();
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (5)
backend/package-lock.json
is excluded by!**/package-lock.json
frontend/package-lock.json
is excluded by!**/package-lock.json
frontend/public/favicon.ico
is excluded by!**/*.ico
frontend/public/logo192.png
is excluded by!**/*.png
frontend/public/logo512.png
is excluded by!**/*.png
📒 Files selected for processing (85)
.gitattributes
(1 hunks).github/workflows/ci.yml
(1 hunks)Dockerfile.backend
(1 hunks)Dockerfile.frontend
(1 hunks)PULL_REQUEST.md
(1 hunks)README.md
(1 hunks)backend/.eslintrc.js
(1 hunks)backend/.gitignore
(1 hunks)backend/.prettierrc
(1 hunks)backend/README.md
(1 hunks)backend/nest-cli.json
(1 hunks)backend/package.json
(1 hunks)backend/src/app.controller.spec.ts
(1 hunks)backend/src/app.controller.ts
(1 hunks)backend/src/app.module.ts
(1 hunks)backend/src/app.service.ts
(1 hunks)backend/src/config/corsConfig.ts
(1 hunks)backend/src/config/databaseConfig.ts
(1 hunks)backend/src/config/id-formatter.ts
(1 hunks)backend/src/main.ts
(1 hunks)backend/src/modules/todos/controllers/todo.controller.ts
(1 hunks)backend/src/modules/todos/dto/create-todo.dto.ts
(1 hunks)backend/src/modules/todos/dto/update-todo.dto.ts
(1 hunks)backend/src/modules/todos/interfaces/todo.interface.ts
(1 hunks)backend/src/modules/todos/module/todo.module.ts
(1 hunks)backend/src/modules/todos/schemas/todo.schema.ts
(1 hunks)backend/src/modules/todos/services/todo.service.ts
(1 hunks)backend/test/app.e2e-spec.ts
(1 hunks)backend/test/jest-e2e.json
(1 hunks)backend/test/todo.service.spec.ts
(1 hunks)backend/tsconfig.build.json
(1 hunks)backend/tsconfig.json
(1 hunks)docker-compose.yml
(1 hunks)er.name
(1 hunks)frontend/.eslintrc
(1 hunks)frontend/.gitignore
(1 hunks)frontend/.prettierrc
(1 hunks)frontend/package.json
(2 hunks)frontend/src/App.tsx
(1 hunks)frontend/src/components/Card/index.tsx
(1 hunks)frontend/src/components/DeleteModal/DeleteModalStyles.scss
(1 hunks)frontend/src/components/DeleteModal/index.tsx
(1 hunks)frontend/src/components/Search/index.tsx
(1 hunks)frontend/src/components/SearchBar/index.tsx
(1 hunks)frontend/src/components/Todo/index.tsx
(1 hunks)frontend/src/components/TodoCard/TodoCard.test.tsx
(1 hunks)frontend/src/components/TodoCard/index.tsx
(1 hunks)frontend/src/components/TodoFilters/index.tsx
(1 hunks)frontend/src/components/TodoForm/index.tsx
(1 hunks)frontend/src/components/index.tsx
(1 hunks)frontend/src/index.tsx
(1 hunks)frontend/src/lib/api.ts
(1 hunks)frontend/src/pages/TodoPage.tsx
(1 hunks)frontend/src/pages/Vehicles/index.tsx
(1 hunks)frontend/src/services/DeleteModalServices.tsx
(1 hunks)frontend/src/services/SearchBarServices.tsx
(1 hunks)frontend/src/services/TodoCardServices.tsx
(1 hunks)frontend/src/services/TodoFormServices.tsx
(1 hunks)frontend/src/services/TodoServices.tsx
(1 hunks)frontend/src/styles/SearchBarStyles.scss
(1 hunks)frontend/src/styles/TodoCardStyles.scss
(1 hunks)frontend/src/styles/TodoFiltersStyles.scss
(1 hunks)frontend/src/styles/TodoFormStyles.scss
(1 hunks)frontend/src/styles/TodoStyles.scss
(1 hunks)frontend/src/styles/global.scss
(1 hunks)frontend/src/styles/theme.scss
(1 hunks)frontend/src/types/CardTypes.tsx
(1 hunks)frontend/src/types/DeleteModalTypes.tsx
(1 hunks)frontend/src/types/SearchBarTypes.tsx
(1 hunks)frontend/src/types/SearchTypes.tsx
(1 hunks)frontend/src/types/TodoCardTypes.tsx
(1 hunks)frontend/src/types/TodoFiltersTypes.tsx
(1 hunks)frontend/src/types/TodoFormTypes.tsx
(1 hunks)frontend/src/types/TodoTypes.tsx
(1 hunks)frontend/src/utils/DeleteModalUtils.tsx
(1 hunks)frontend/src/utils/SearchBarUtils.tsx
(1 hunks)frontend/src/utils/TodoCardUtils.tsx
(1 hunks)frontend/src/utils/TodoFiltersUtils.tsx
(1 hunks)frontend/src/utils/TodoFormUtils.tsx
(1 hunks)frontend/src/utils/TodoUtils.tsx
(1 hunks)frontend/src/utils/useDeleteModal.tsx
(1 hunks)nginx.conf
(1 hunks)src/components/Search/index.tsx
(0 hunks)src/components/index.tsx
(0 hunks)src/lib/api.ts
(0 hunks)
💤 Files with no reviewable changes (3)
- src/components/Search/index.tsx
- src/components/index.tsx
- src/lib/api.ts
🧰 Additional context used
🧬 Code Definitions (15)
frontend/src/types/TodoFormTypes.tsx (1)
frontend/src/types/TodoTypes.tsx (1)
ITodo
(1-8)
backend/src/main.ts (1)
backend/src/config/corsConfig.ts (1)
corsConfig
(4-9)
frontend/src/components/DeleteModal/index.tsx (1)
frontend/src/types/DeleteModalTypes.tsx (1)
DeleteModalProps
(1-6)
frontend/src/utils/TodoUtils.tsx (1)
frontend/src/types/TodoTypes.tsx (1)
ITodo
(1-8)
frontend/src/types/TodoCardTypes.tsx (1)
frontend/src/types/TodoTypes.tsx (1)
ITodo
(1-8)
backend/src/modules/todos/module/todo.module.ts (2)
backend/src/config/databaseConfig.ts (1)
databaseConfig
(5-5)backend/src/modules/todos/schemas/todo.schema.ts (1)
TodoSchema
(25-25)
frontend/src/services/TodoCardServices.tsx (2)
frontend/src/types/TodoTypes.tsx (1)
ITodo
(1-8)frontend/src/lib/api.ts (2)
put
(21-24)del
(26-29)
frontend/src/services/DeleteModalServices.tsx (1)
frontend/src/lib/api.ts (1)
del
(26-29)
frontend/src/services/TodoFormServices.tsx (2)
frontend/src/types/TodoTypes.tsx (1)
ITodo
(1-8)frontend/src/lib/api.ts (2)
post
(16-19)put
(21-24)
frontend/src/pages/Vehicles/index.tsx (2)
frontend/src/types/Vehicle.ts (1)
IVehicle
(1-11)frontend/src/lib/api.ts (1)
get
(11-14)
frontend/src/components/Todo/index.tsx (3)
frontend/src/services/TodoServices.tsx (2)
useTodos
(9-16)invalidateTodosCache
(18-20)frontend/src/types/TodoTypes.tsx (1)
ITodo
(1-8)frontend/src/components/index.tsx (4)
SearchBar
(11-11)TodoFilters
(11-11)TodoCard
(11-11)TodoForm
(11-11)
frontend/src/utils/TodoCardUtils.tsx (1)
frontend/src/types/TodoTypes.tsx (1)
ITodo
(1-8)
frontend/src/components/TodoForm/index.tsx (2)
frontend/src/types/TodoFormTypes.tsx (1)
TodoFormProps
(3-7)frontend/src/utils/TodoFormUtils.tsx (1)
useTodoForm
(15-57)
backend/src/modules/todos/controllers/todo.controller.ts (2)
backend/src/modules/todos/dto/create-todo.dto.ts (1)
CreateTodoDto
(4-20)backend/src/modules/todos/dto/update-todo.dto.ts (1)
UpdateTodoDto
(4-20)
frontend/src/utils/TodoFormUtils.tsx (1)
frontend/src/types/TodoTypes.tsx (1)
ITodo
(1-8)
🪛 YAMLlint (1.35.1)
docker-compose.yml
[error] 46-46: no new line character at the end of file
(new-line-at-end-of-file)
[error] 46-46: trailing spaces
(trailing-spaces)
.github/workflows/ci.yml
[error] 15-15: trailing spaces
(trailing-spaces)
[error] 20-20: trailing spaces
(trailing-spaces)
[error] 25-25: trailing spaces
(trailing-spaces)
[error] 30-30: trailing spaces
(trailing-spaces)
[error] 35-35: trailing spaces
(trailing-spaces)
[error] 46-46: trailing spaces
(trailing-spaces)
[error] 51-51: trailing spaces
(trailing-spaces)
[error] 56-56: trailing spaces
(trailing-spaces)
[error] 61-61: trailing spaces
(trailing-spaces)
[error] 66-66: trailing spaces
(trailing-spaces)
[error] 79-79: trailing spaces
(trailing-spaces)
[error] 82-82: trailing spaces
(trailing-spaces)
[error] 90-90: trailing spaces
(trailing-spaces)
[error] 97-97: no new line character at the end of file
(new-line-at-end-of-file)
[error] 97-97: trailing spaces
(trailing-spaces)
🪛 Gitleaks (8.21.2)
backend/README.md
5-5: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
🪛 LanguageTool
backend/README.md
[style] ~76-~76: The expression “come in handy” is rather colloquial. Consider replacing it to add a more formal tone to your writing.
Context: ...ces Check out a few resources that may come in handy when working with NestJS: - Visit the ...
(COME_IN_HANDY)
[uncategorized] ~89-~89: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...). ## Support Nest is an MIT-licensed open source project. It can grow thanks to the spon...
(EN_COMPOUND_ADJECTIVE_INTERNAL)
[style] ~89-~89: Consider using a more formal and expressive alternative to ‘amazing’.
Context: ...anks to the sponsors and support by the amazing backers. If you'd like to join them, pl...
(AWESOME)
README.md
[inconsistency] ~65-~65: O URL contém o caratére inválido segundo RFC 1738. Os caratéres especiais podem ser codificados com % seguido de dois números hexadecimais. Context: ...ev ``` O servidor estará disponível em
http://localhost:3333`. ### Frontend 1. Navegue até a pasta d...
(URL_VALIDATION)
[inconsistency] ~84-~84: O URL contém o caratére inválido segundo RFC 1738. Os caratéres especiais podem ser codificados com % seguido de dois números hexadecimais. Context: ...t ``` A aplicação estará disponível em
http://localhost:3000`. ## Extras implementados ### 1. Tipage...
(URL_VALIDATION)
PULL_REQUEST.md
[locale-violation] ~139-~139: “performance” é um estrangeirismo. É preferível dizer “desempenho”, “atuação”, “apresentação”, “espetáculo” ou “interpretação”.
Context: ...o em qualidade de código, usabilidade e performance. Adicionei vários extras para demonstra...
(PT_BARBARISMS_REPLACE_PERFORMANCE)
🪛 markdownlint-cli2 (0.17.2)
backend/README.md
5-5: Link and image reference definitions should be needed
Unused link or image reference definition: "circleci-image"
(MD053, link-image-reference-definitions)
6-6: Link and image reference definitions should be needed
Unused link or image reference definition: "circleci-url"
(MD053, link-image-reference-definitions)
32-32: Dollar signs used before commands without showing output
null
(MD014, commands-show-output)
68-68: Dollar signs used before commands without showing output
null
(MD014, commands-show-output)
69-69: Dollar signs used before commands without showing output
null
(MD014, commands-show-output)
README.md
49-49: Fenced code blocks should have a language specified
null
(MD040, fenced-code-language)
54-54: Fenced code blocks should have a language specified
null
(MD040, fenced-code-language)
61-61: Fenced code blocks should have a language specified
null
(MD040, fenced-code-language)
70-70: Fenced code blocks should have a language specified
null
(MD040, fenced-code-language)
75-75: Fenced code blocks should have a language specified
null
(MD040, fenced-code-language)
80-80: Fenced code blocks should have a language specified
null
(MD040, fenced-code-language)
101-101: Fenced code blocks should have a language specified
null
(MD040, fenced-code-language)
109-109: Fenced code blocks should have a language specified
null
(MD040, fenced-code-language)
125-125: Fenced code blocks should have a language specified
null
(MD040, fenced-code-language)
139-139: Fenced code blocks should have a language specified
null
(MD040, fenced-code-language)
🪛 Biome (1.9.4)
backend/src/config/id-formatter.ts
[error] 4-38: Avoid classes that contain only static members.
Prefer using simple functions instead of classes with only static members.
(lint/complexity/noStaticOnlyClass)
🪛 actionlint (1.7.4)
.github/workflows/ci.yml
14-14: the runner of "actions/checkout@v2" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
17-17: the runner of "actions/setup-node@v2" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
45-45: the runner of "actions/checkout@v2" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
48-48: the runner of "actions/setup-node@v2" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
78-78: the runner of "actions/checkout@v2" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
81-81: the runner of "docker/setup-buildx-action@v1" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
84-84: the runner of "docker/build-push-action@v2" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
92-92: the runner of "docker/build-push-action@v2" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
🔇 Additional comments (74)
backend/tsconfig.build.json (1)
1-4
: Configuration File Setup VerifiedThe new
tsconfig.build.json
file correctly extends the base configuration fromtsconfig.json
and excludes folders and file patterns (such asnode_modules
,test
,dist
, and**/*spec.ts
) that are not needed for the build. This setup will help streamline the build process by omitting extraneous files.backend/nest-cli.json (1)
1-9
: Configuration File Valid and ConsistentThe JSON configuration adheres to the NestJS CLI schema and correctly specifies the essential fields:
- The
$schema
property properly points to "https://json.schemastore.org/nest-cli".- The
collection
is set to "@nestjs/schematics", maintaining consistency with common NestJS practices.- The
sourceRoot
being "src" is standard for project structure.- The
compilerOptions
with"deleteOutDir": true
is useful for ensuring a clean build each time.Overall, the file is well-structured and follows best practices for a NestJS CLI configuration.
frontend/.gitignore (6)
1-2
: Informative Header CommentThe header clearly references GitHub’s documentation on ignoring files, which is helpful for quick reference.
3-7
: Dependency Ignore PatternsThe dependencies block correctly ignores common directories and files—such as
/node_modules
,/.pnp
, and.pnp.js
—that should not be tracked in version control. This helps maintain a clean repository.
8-10
: Testing Artifacts ExclusionThe test section effectively excludes the
/coverage
directory generated during testing, ensuring these artifacts do not clutter the repository.
11-13
: Production Build ArtifactsThe production block accurately ignores the
/build
directory, which is standard for build outputs in many frontend projects.
14-20
: Miscellaneous File ExclusionsThe miscellaneous section reliably covers system-generated files (like
.DS_Store
) and various environment configuration files. This is important to prevent sensitive configurations from being committed.
21-23
: Log Files ExclusionThe patterns for log files (
npm-debug.log*
,yarn-debug.log*
, andyarn-error.log*
) are well chosen to keep debugging logs out of version control.backend/.prettierrc (1)
1-11
: Well-configured Prettier setup that follows best practicesThe Prettier configuration follows modern JavaScript/TypeScript best practices with sensible defaults. The settings like single quotes, trailing commas, 2-space indentation, and 100 character line width provide a good balance between readability and consistency.
backend/.gitignore (1)
1-56
: Comprehensive .gitignore with well-organized sectionsThis .gitignore file is thorough and well-organized, covering all standard files and directories that should be excluded from version control in a Node.js/NestJS project.
The file correctly handles:
- Build outputs and dependencies
- Logs and debug files
- Testing artifacts
- IDE-specific files (while allowing useful VS Code settings)
- Environment files for security
- Temporary and runtime files
backend/.eslintrc.js (1)
1-33
: Well-balanced ESLint configuration for TypeScript developmentThe ESLint configuration strikes a good balance between enforcing best practices and not being overly restrictive, with appropriate integration of Prettier.
Notable strengths:
- Good TypeScript integration with sensible rule customizations
- Balanced approach to linting severity (warnings vs errors)
- Proper console usage restrictions with allowances for warnings/errors
- Strong code quality rules like no-duplicate-imports and prefer-const
This configuration supports the project's goal of maintaining high code quality.
backend/README.md (1)
5-6
:❓ Verification inconclusive
Security & Unused Reference Definitions: Exposed Token & Unused Link References
The image reference on line 5 includes a token (
token=abc123def456
). If this token were real or grants any privileges, it may expose sensitive information. Consider removing or securing it (e.g., by using environment variables or a configuration file). Also, the link reference on line 6 ([circleci-url]
) isn’t apparently used in the document. Either incorporate it into the README where needed or remove it to clean up the file.
Attention: Exposed Token & Unused Link Reference in README
- The CircleCI image reference (line 5) embeds a token (
token=abc123def456
). Verify whether this token is live or merely a placeholder. If it’s active—or could be accidentally activated in a real scenario—please remove it or secure it using environment variables/configuration files.- The
[circleci-url]
definition (line 6) isn’t referenced anywhere in the document. If it’s not used, consider removing it to avoid confusion; otherwise, integrate it appropriately in the README.🧰 Tools
🪛 Gitleaks (8.21.2)
5-5: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
🪛 markdownlint-cli2 (0.17.2)
5-5: Link and image reference definitions should be needed
Unused link or image reference definition: "circleci-image"(MD053, link-image-reference-definitions)
6-6: Link and image reference definitions should be needed
Unused link or image reference definition: "circleci-url"(MD053, link-image-reference-definitions)
frontend/src/utils/SearchBarUtils.tsx (1)
1-4
: Structural Placeholder: Maintaining Project ConsistencyThis file serves as a structural placeholder for potential future utilities related to the SearchBar component. The comments clearly explain its purpose. If no utilities are needed at present, this minimal export is acceptable.
frontend/src/services/SearchBarServices.tsx (1)
1-5
: Structural Placeholder: Consistent Service StructureThe file is correctly implemented as a placeholder for SearchBar-related services, explaining that no direct API calls are needed. This maintains the project’s modular structure.
frontend/src/pages/TodoPage.tsx (1)
1-8
: TodoPage Component: Clean and Focused ImplementationThe new
TodoPage
component (lines 1–8) is straightforward and correctly imports and renders theTodo
component. The use ofReact.FC
is acceptable here. In future iterations, you might consider enhancing this component with error boundaries or loading states if the todo list operations become more complex..gitattributes (1)
1-3
: Line Ending Normalization: Correct ConfigurationThe new
.gitattributes
file properly enforces LF normalization across text files, which will help maintain consistency across different operating systems. The configuration is correct.frontend/src/types/CardTypes.tsx (1)
1-6
: Well-structured type definition!The
ICardProps
interface is properly defined with appropriate types for a React card component. UsingReactNode
for the children prop follows React best practices for allowing any valid JSX content.frontend/src/App.tsx (1)
2-9
: Clean and straightforward App component.The App component is well-structured and follows React best practices by using a functional component approach. It serves as a clear entry point to the application by rendering the main TodoPage component.
frontend/src/components/index.tsx (1)
2-11
: Good use of barrel file pattern for component exports.This implementation provides a clean and convenient way to import multiple components from a single location, which improves code organization and readability.
frontend/src/types/SearchBarTypes.tsx (1)
1-5
: Ensure consistency with ISearchProps interfaceI noticed inconsistencies between this
SearchBarProps
interface and theISearchProps
interface inSearchTypes.tsx
. They seem to serve similar purposes but have different signatures for theonChange
function. Consider consolidating these interfaces or ensuring they're used for different, well-defined purposes.Are both interfaces needed in your application, or could you consolidate them to maintain consistency? If they serve different purposes, make sure your component implementations align with these different type definitions.
backend/src/app.module.ts (1)
7-11
: The module structure looks goodThe NestJS module is well-structured with proper imports, controllers, and providers. Good job following the NestJS architectural patterns.
backend/src/app.service.ts (1)
1-8
: Simple service implementation looks goodThis is a standard NestJS service with a basic "Hello World" method. The implementation follows the framework's conventions with proper decorator usage.
Dockerfile.backend (1)
5-7
: Use separate steps for package.json and dependency installationThis approach properly leverages Docker's layer caching by copying only package files first and installing dependencies before copying the rest of the code.
frontend/src/styles/theme.scss (1)
3-24
: Good implementation of dark theme variablesThe dark theme variables are well-organized and comprehensive, covering all necessary UI elements with consistent naming patterns.
frontend/src/types/TodoFormTypes.tsx (1)
1-7
: Well-defined interface with appropriate type constraintsThe
TodoFormProps
interface is well designed:
- The
todo
property is correctly marked as optional for creating new todos- The
onSave
function smartly omitsid
andcreatedAt
from the saved todo since these are typically managed server-side- The
onCancel
function has a clear return type signatureThis accurately models the component's requirements.
backend/src/app.controller.spec.ts (1)
1-22
: Well-structured unit test for AppControllerThis is a clean and concise unit test for the AppController. The test properly sets up the testing module with the necessary controller and service dependencies, and verifies that the controller's
getHello()
method returns the expected "Hello World!" string.frontend/src/types/TodoFiltersTypes.tsx (1)
1-6
: Good TypeScript interface definitionThe
TodoFiltersProps
interface is well-defined with clear type annotations for filter-related properties. It properly includes both state values (selectedColor
,showOnlyFavorites
) and their corresponding handler functions (onColorSelect
,onFavoriteToggle
) with appropriate function signatures.frontend/.prettierrc (1)
1-13
: Configuration looks good with sensible defaultsThe Prettier configuration has good defaults that align with modern JavaScript/TypeScript practices. The choices for semicolons, single quotes, tab width of 2, and other formatting options will help maintain consistent code style throughout the project.
frontend/src/utils/TodoUtils.tsx (2)
1-3
: Clean imports with appropriate naming conventionsThe imports are well-organized, separating types from services with clear naming.
4-10
: Well-implemented sorting utility with immutabilityGood implementation of the
sortTodosByFavorite
function. The use of spread operator to create a new array preserves immutability, which is important for React applications. The sorting logic is clear and correctly prioritizes favorites.backend/src/modules/todos/dto/create-todo.dto.ts (1)
4-20
: Well-structured DTO with proper validationThe DTO uses appropriate class-validator decorators to enforce validation rules for the todo entity. The properties and types are well-defined with good validation constraints.
backend/src/app.controller.ts (1)
1-12
: Well-structured controller implementationThe AppController follows best practices with proper dependency injection and clear method structure. The controller is focused and concise.
backend/test/jest-e2e.json (1)
1-9
: Appropriate Jest E2E testing configurationThe Jest configuration is properly set up for E2E testing with the correct file extensions, test environment, and transformation settings.
backend/src/modules/todos/module/todo.module.ts (2)
9-16
: Well-structured module with proper component registrationThe module follows NestJS best practices with appropriate imports, controller, and provider registration. The structure is clean and organized.
7-7
:❓ Verification inconclusive
Verify database configuration implementation
The imported
databaseConfig
appears to be an empty object based on the code snippet provided. Ensure that this configuration is properly implemented with any necessary MongoDB connection options.
🏁 Script executed:
#!/bin/bash # Check the implementation of databaseConfig cat backend/src/config/databaseConfig.tsLength of output: 254
Database Configuration Verification
The file
backend/src/config/databaseConfig.ts
intentionally exports an empty object (with the comment “Objeto vazio para usar apenas as configurações padrão”) so that Mongoose’s default configurations are used. If this is the desired behavior, no changes are required. However, if you plan to override or enhance MongoDB connection options (for example, using custom values foruseNewUrlParser
,useUnifiedTopology
, or connection timeouts), please update this file accordingly and add a clarifying comment for future maintainers.frontend/src/types/TodoCardTypes.tsx (1)
1-8
: Well-defined TypeScript interface with clear callback signatures.The TodoCardProps interface correctly defines the structure for the TodoCard component props, with proper typing for the todo object and callback functions. This approach ensures type safety throughout the application and makes component interactions explicit.
backend/src/modules/todos/schemas/todo.schema.ts (1)
7-23
: Well-structured Todo schema with proper field definitions.The schema is well-designed with appropriate field types, required constraints, and default values. The timestamp configuration is correctly set to track creation time only.
Dockerfile.frontend (1)
3-11
: Good use of Docker caching for dependencies.The Dockerfile correctly follows best practices by copying package files first, installing dependencies, then copying the rest of the code. This approach optimizes build times by leveraging Docker's layer caching.
frontend/package.json (3)
6-6
: Good choice of modern dependencies for frontend functionality.The addition of React Query (@tanstack/react-query) for data fetching, Axios for HTTP requests, and React Icons for UI elements are appropriate choices for a modern React application.
Also applies to: 14-14, 18-18
56-59
: Good addition of code quality tools.Adding Prettier and ESLint integration helps maintain code quality and consistency across the project.
15-15
:✅ Verification successful
Consider replacing deprecated node-sass.
The
node-sass
package is deprecated and may cause compatibility issues in the future. Consider replacing it with the maintainedsass
package.- "node-sass": "^7.0.1", + "sass": "^1.71.1",
🌐 Web query:
Is node-sass deprecated? What is the recommended alternative?
💡 Result:
Node Sass has been deprecated as of July 2024, with its npm package marked as deprecated and GitHub repository archived. The LibSass engine it relied on also remains deprecated[3][5]. This decision followed years of declining maintenance and an inability to keep up with modern CSS and Sass features[3][5].
The recommended alternative is Dart Sass, the primary Sass implementation actively maintained by the Sass team. Key advantages include:
- Full support for modern Sass/CSS features like
calc()
and modules[1][3]- No native bindings or compilation dependencies[1][8]
- Active development with regular updates[3][8]
Migration steps from Node Sass to Dart Sass:
- Remove Node Sass dependencies:
npm uninstall node-sass- Install Dart Sass:
npm install --save-dev sass- Update build scripts:
- Change
node-sass
tosass
in package.json[1]- Replace
--output-style compressed
with--style=compressed
[1]- Add
--load-path=node_modules/
for module resolution[1]Performance comparisons show Dart Sass (native Dart VM) compiles faster than both Node Sass and its JavaScript implementation, though the JS version remains slower than LibSass in some scenarios[8]. For most projects, the maintainability and feature advantages outweigh minor performance tradeoffs[3][8].
Citations:
- 1: https://dev.to/ccreusat/migrating-from-node-sass-to-sass-dart-sass-with-npm-3g3c
- 2: https://stackshare.io/node-sass/alternatives
- 3: https://sass-lang.com/blog/node-sass-is-end-of-life/
- 4: https://www.fynd.academy/blog/node-sass
- 5: https://www.npmjs.com/package/node-sass
- 6: https://npm-compare.com/grunt-sass,gulp-sass,node-sass,sass
- 7: https://www.drupal.org/project/varbase_heroslider_media/issues/3272442
- 8: https://itnext.io/the-css-preprocessor-dilemma-node-sass-or-dart-sass-32a0a096572
- 9: node-sass is deprecated nodejs/help#4191
- 10: https://sass-lang.com/blog/libsass-is-deprecated/
Update Dependency to Dart Sass
The current dependency onnode-sass
(version ^7.0.1) is deprecated. It is recommended to replace it with Dart Sass via thesass
package. Please update yourfrontend/package.json
accordingly:- "node-sass": "^7.0.1", + "sass": "^1.71.1",Additionally, ensure that any associated build scripts or configuration files that reference
node-sass
are updated to usesass
. This change will help avoid future compatibility issues and take advantage of the enhanced features provided by Dart Sass.frontend/src/components/DeleteModal/index.tsx (1)
6-8
: Good use of conditional rendering for the modal.The component correctly uses conditional rendering with an early return when
isOpen
is false, which is an efficient approach.frontend/.eslintrc (1)
1-38
: Well-structured ESLint configuration for TypeScript and ReactThe ESLint configuration is comprehensive and follows modern React and TypeScript development best practices. It uses appropriate extensions, plugins, and rules while maintaining a good balance between strictness and practicality (using warnings instead of errors for some rules).
I particularly like:
- Disabling React import requirement (appropriate for React 17+)
- Proper enforcement of React hooks rules
- Ignoring unused variables that start with underscore (good convention)
- Disabling prop-types (redundant with TypeScript)
frontend/src/pages/Vehicles/index.tsx (2)
11-11
: Good addition of loading stateAdding a loading state variable is good practice for handling asynchronous operations.
15-22
: Improved error handling with try-catch-finallyThe implementation of proper error handling is a good improvement. The try-catch-finally pattern ensures:
- Errors are properly caught and logged
- Loading state is correctly updated regardless of success or failure
frontend/src/styles/global.scss (2)
12-14
: Good use of CSS variables for themingUsing CSS variables for theming is an excellent choice as it enables easy theme switching (light/dark modes) without duplicate style definitions.
31-59
: Well-implemented animations enhance UXThe defined animations and their application to specific components create a polished user experience. The timing and easing functions are appropriate for subtle motion that doesn't distract from the content.
nginx.conf (1)
1-20
: Good Nginx configuration for a React SPA with API proxyThe configuration is well-structured for a single-page application with separate frontend and backend services:
- Correctly set up to serve static files and handle client-side routing with the fallback to index.html
- Properly configured proxy for API requests with appropriate headers for WebSocket support
- Good separation between frontend and backend concerns
frontend/src/utils/TodoFiltersUtils.tsx (3)
3-11
: Well-structured color palette with clear comments!The color constants are well-defined with helpful comments indicating color names. This makes it easy to understand the available palette for todo items.
13-16
: Clean and effective filter implementation.The
filterTodosByColor
function correctly handles both the null case and filtering by specific color. The implementation is concise and follows functional programming best practices.
18-21
: Good implementation of the favorites filter.The
filterTodosByFavorite
function is well-implemented, efficiently handling the conditional filtering of todos.frontend/src/components/TodoCard/TodoCard.test.tsx (6)
6-13
: Well-structured mock data for testing.The mock todo object contains all necessary properties with appropriate test values, providing a good foundation for the test cases.
19-22
: Good test isolation practice.Using
beforeEach
withjest.clearAllMocks()
ensures that each test starts with a clean slate, preventing interference between tests.
24-36
: Comprehensive rendering test.This test effectively verifies that the component renders the expected content, ensuring the basic display functionality works correctly.
38-52
: Well-implemented interaction test for favorites.The test correctly verifies that clicking the favorite button triggers the callback with the right parameters. Using
getByLabelText
is a good accessibility practice.
54-68
: Effective edit functionality test.This test properly verifies that the edit button works correctly and passes the right data to the callback function.
70-86
: Good modal interaction testing.The test effectively verifies that clicking the delete button opens the confirmation modal with the correct content. The expectations are clear and meaningful.
frontend/src/components/DeleteModal/DeleteModalStyles.scss (2)
1-12
: Well-implemented modal overlay.The overlay covers the entire viewport with a semi-transparent background and centers its content, providing a good user experience when displaying the modal.
14-31
: Responsive modal content with good styling.The modal content has appropriate styling with good spacing, colors, and a responsive width that works well on different screen sizes.
backend/src/config/id-formatter.ts (1)
28-30
: Simple and effective toString method.This method correctly converts an ObjectId to its string representation.
frontend/src/index.tsx (1)
4-6
: Good implementation of React Query.The integration of React Query with a QueryClientProvider is well done. This will provide efficient data fetching, caching, and state management for your Todo application.
Also applies to: 9-19
frontend/src/components/TodoForm/index.tsx (1)
6-78
: Well-structured form component.The TodoForm component is well-organized and makes good use of custom hooks for state management. Props are properly typed using the TodoFormProps interface, and the form includes all necessary fields with appropriate validation.
frontend/src/utils/TodoFormUtils.tsx (1)
15-57
: Well-structured custom hook.The useTodoForm hook is well-implemented with clear state management and appropriate side effects for initializing from an existing todo. The form validation is simple but effective for the requirements.
frontend/src/styles/TodoFiltersStyles.scss (2)
4-101
: Well-structured filter component with good UX considerationsThe filter component styling is well-organized with proper nesting, variable usage, and interactive elements. The hover and selection states provide good visual feedback to users.
59-100
:✅ Verification successful
Ensure checkbox has an accessible label
The current implementation styles a checkbox and its label, but make sure they're properly associated in the HTML with a
for
attribute that matches the checkbox'sid
.
🏁 Script executed:
#!/bin/bash # Search for the favorite filter implementation to verify proper label association rg -A 10 "favorite-filter" --type tsxLength of output: 65
I'll generate a new verification script that searches TSX files by explicitly specifying the file pattern rather than the file type. This should help us locate the code where the checkbox and its label are rendered.
Please run this script so we can verify if the checkbox and label are properly associated (i.e., the input's id matches the label’s for attribute).
🏁 Script executed:
#!/bin/bash # Search for the favorite filter implementation in TSX files to verify proper label association rg -A 10 "favorite-filter" -g "*.tsx"Length of output: 1016
Accessible Label Verified: No changes required
The
<input>
and<label>
elements infrontend/src/components/TodoFilters/index.tsx
are properly associated using theid="show-favorites"
on the input andhtmlFor="show-favorites"
on the label.
- File verified:
frontend/src/components/TodoFilters/index.tsx
- Verification result: Proper accessible label is already implemented.
frontend/src/styles/TodoStyles.scss (1)
4-143
: Well-structured and visually appealing stylingThe TodoStyles provide a cohesive design with good visual hierarchy, animations, and consistent use of CSS variables for theming. The BEM-like naming convention makes the code easy to maintain.
README.md (1)
1-155
: LGTM! Comprehensive and well-structured README.The README provides clear, detailed information about the project structure, technologies, functionalities, and setup instructions. It's well-organized with proper headings and lists, making it easy to follow.
🧰 Tools
🪛 LanguageTool
[inconsistency] ~65-~65: O URL contém o caratére inválido
segundo RFC 1738. Os caratéres especiais podem ser codificados com % seguido de dois números hexadecimais. Context: ...ev ``` O servidor estará disponível em
http://localhost:3333`. ### Frontend 1. Navegue até a pasta d...(URL_VALIDATION)
[inconsistency] ~84-~84: O URL contém o caratére inválido
segundo RFC 1738. Os caratéres especiais podem ser codificados com % seguido de dois números hexadecimais. Context: ...t ``` A aplicação estará disponível em
http://localhost:3000`. ## Extras implementados ### 1. Tipage...(URL_VALIDATION)
🪛 markdownlint-cli2 (0.17.2)
49-49: Fenced code blocks should have a language specified
null(MD040, fenced-code-language)
54-54: Fenced code blocks should have a language specified
null(MD040, fenced-code-language)
61-61: Fenced code blocks should have a language specified
null(MD040, fenced-code-language)
70-70: Fenced code blocks should have a language specified
null(MD040, fenced-code-language)
75-75: Fenced code blocks should have a language specified
null(MD040, fenced-code-language)
80-80: Fenced code blocks should have a language specified
null(MD040, fenced-code-language)
101-101: Fenced code blocks should have a language specified
null(MD040, fenced-code-language)
109-109: Fenced code blocks should have a language specified
null(MD040, fenced-code-language)
125-125: Fenced code blocks should have a language specified
null(MD040, fenced-code-language)
139-139: Fenced code blocks should have a language specified
null(MD040, fenced-code-language)
backend/src/modules/todos/controllers/todo.controller.ts (1)
2-36
: LGTM! Well-structured controller following RESTful principles.The controller is well organized with clear method signatures and appropriate HTTP verb decorators. It follows the standard RESTful API design pattern with proper endpoint definitions.
frontend/src/styles/TodoCardStyles.scss (2)
4-111
: Great job with the detailed, well-structured SCSS.The styles for the todo card are comprehensive, with careful attention to hover states, animations, and responsive design. The use of CSS variables enables theming support, and the BEM-like naming convention makes the code easy to understand and maintain.
6-9
: Excellent use of transitions and hover effects.The subtle animations and hover effects (translation, shadow changes, scaling) create a polished and interactive UI. The use of CSS variables for colors supports the theme switching functionality mentioned in the PR summary.
Also applies to: 14-17, 19-27, 50-53, 95-99, 101-107
PULL_REQUEST.md (3)
1-148
: Excellent PR documentation with comprehensive details!This is an exceptionally well-documented pull request that clearly outlines all the implementations, technologies used, and extra features added. The structured format with sections for frontend, backend, and extras makes it easy to understand the scope of changes.
🧰 Tools
🪛 LanguageTool
[locale-violation] ~139-~139: “performance” é um estrangeirismo. É preferível dizer “desempenho”, “atuação”, “apresentação”, “espetáculo” ou “interpretação”.
Context: ...o em qualidade de código, usabilidade e performance. Adicionei vários extras para demonstra...(PT_BARBARISMS_REPLACE_PERFORMANCE)
51-85
: Impressive list of extra implementations.The additional implementations beyond the core requirements demonstrate a high level of commitment and technical proficiency. The inclusion of TypeScript interfaces, ESLint/Prettier configurations, Docker setup, tests, and CI/CD pipeline is particularly noteworthy.
86-110
: Great attention to UI/UX details.The detailed descriptions of UI components and interactions show careful consideration for user experience. Features like animations, color selectors, and theme toggle with smooth transitions enhance the application's usability.
backend/src/modules/todos/services/todo.service.ts (1)
10-15
: Dependency injection setup looks good.Using
@Injectable()
and injecting thetodoModel
through the constructor appropriately follows NestJS conventions.
// Objeto vazio para usar apenas as configurações padrão | ||
export const databaseConfig: MongooseModuleOptions = {}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Consider adding more explicit configuration or documentation.
The empty configuration object is valid if you're relying on defaults, but it might make it harder to understand the actual connection details being used. Consider:
- Adding more explicit configuration to improve clarity
- Including detailed comments about what default values are being used
- Using English for comments to maintain consistency with code and identifiers
- // Objeto vazio para usar apenas as configurações padrão
+ // Empty object to use default configuration only
+ // Default connection: mongodb://127.0.0.1:27017/[project-name]
export const databaseConfig: MongooseModuleOptions = {};
🏁 Script executed:
#!/bin/bash
# Check where this configuration is used to understand the context better
rg -A 5 "databaseConfig" --type typescript
Length of output: 77
Action Needed: Enhance Clarity in databaseConfig
Usage
Please update the empty configuration object to include explicit documentation clarifying that default connection parameters are being relied upon. In particular:
- Document the assumed default values (e.g., default MongoDB URI such as
mongodb://127.0.0.1:27017/[project-name]
). - Use English in comments to maintain consistency with the codebase.
- Verify manually that the usage of
databaseConfig
across the repository aligns with these documented defaults.
Here's the proposed diff:
- // Objeto vazio para usar apenas as configurações padrão
+ // Empty object to use default configuration only
+ // Default connection: mongodb://127.0.0.1:27017/[project-name]
export const databaseConfig: MongooseModuleOptions = {};
Please run the above script to check all occurrences of databaseConfig
and confirm that all usages assume the same defaults. Manual verification of this context will help ensure that the documentation accurately reflects the behavior across the application.
---
🏁 Script executed:
```shell
#!/bin/bash
# Searching for occurrences of "databaseConfig" in the codebase using the recognized file type "ts"
rg -A 5 "databaseConfig" --type ts
Length of output: 1049
Clarify Default Database Configuration
The empty databaseConfig
object is valid if relying on Mongoose's default options. However, its usage in backend/src/modules/todos/module/todo.module.ts
(where the connection URL is explicitly provided as mongodb://localhost:27017/todolist_db
) may leave future readers unclear about which defaults are applied. To improve understandability and consistency, please consider updating the comment as follows:
- // Objeto vazio para usar apenas as configurações padrão
+ // Empty object to use default configuration only.
+ // Default connection is set in MongooseModule.forRoot as: mongodb://localhost:27017/todolist_db
export const databaseConfig: MongooseModuleOptions = {};
Please verify that this documentation aligns with how defaults are expected to work across the codebase.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
// Objeto vazio para usar apenas as configurações padrão | |
export const databaseConfig: MongooseModuleOptions = {}; | |
// Empty object to use default configuration only. | |
// Default connection is set in MongooseModule.forRoot as: mongodb://localhost:27017/todolist_db | |
export const databaseConfig: MongooseModuleOptions = {}; |
export const deleteTodo = async (id: number): Promise<void> => { | ||
return del(`/todos/${id}`); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add error handling to improve robustness.
The current implementation doesn't handle potential errors that might occur during the delete operation. Consider adding try/catch to properly handle errors.
export const deleteTodo = async (id: number): Promise<void> => {
- return del(`/todos/${id}`);
+ try {
+ return await del(`/todos/${id}`);
+ } catch (error) {
+ console.error('Error deleting todo:', error);
+ throw error;
+ }
};
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
export const deleteTodo = async (id: number): Promise<void> => { | |
return del(`/todos/${id}`); | |
}; | |
export const deleteTodo = async (id: number): Promise<void> => { | |
try { | |
return await del(`/todos/${id}`); | |
} catch (error) { | |
console.error('Error deleting todo:', error); | |
throw error; | |
} | |
}; |
const Search = (props: ISearchProps) => { | ||
return ( | ||
<input type="text" placeholder={props.placeholder} value={props.value} /> | ||
); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing onChange handler for the input element.
The Search component is rendering a controlled input (with a value prop) but doesn't include the required onChange handler. This will make the input read-only and prevent users from typing in it. According to the ISearchProps interface mentioned in the summary, there should be an onChange prop that needs to be used here.
const Search = (props: ISearchProps) => {
return (
- <input type="text" placeholder={props.placeholder} value={props.value} />
+ <input
+ type="text"
+ placeholder={props.placeholder}
+ value={props.value}
+ onChange={props.onChange}
+ />
);
};
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
const Search = (props: ISearchProps) => { | |
return ( | |
<input type="text" placeholder={props.placeholder} value={props.value} /> | |
); | |
}; | |
const Search = (props: ISearchProps) => { | |
return ( | |
<input | |
type="text" | |
placeholder={props.placeholder} | |
value={props.value} | |
onChange={props.onChange} | |
/> | |
); | |
}; |
export const corsConfig: CorsOptions = { | ||
origin: 'http://localhost:3000', | ||
methods: ['GET', 'POST', 'PUT', 'DELETE'], | ||
allowedHeaders: ['Content-Type', 'Authorization'], | ||
credentials: true, | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Use environment variables for CORS configuration.
The origin is currently hardcoded to 'http://localhost:3000', which will only work in development. For a more flexible solution that works across different environments (development, staging, production), consider using environment variables.
export const corsConfig: CorsOptions = {
- origin: 'http://localhost:3000',
+ origin: process.env.ALLOWED_ORIGINS?.split(',') || 'http://localhost:3000',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
};
This change allows you to configure multiple allowed origins through an environment variable (comma-separated) while maintaining localhost as a fallback for development.
Make sure to update your environment configuration and documentation to include the new ALLOWED_ORIGINS variable.
Action Required: Update CORS Configuration to Use Environment Variables
The current configuration in backend/src/config/corsConfig.ts uses a hardcoded origin ('http://localhost:3000'
), which only works for development. To support multiple environments (development, staging, production), update this configuration to read from an environment variable. For example, consider the following change:
export const corsConfig: CorsOptions = {
- origin: 'http://localhost:3000',
+ origin: process.env.ALLOWED_ORIGINS?.split(',') || 'http://localhost:3000',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
};
Please update your environment configuration and documentation to include the new ALLOWED_ORIGINS
variable.
🏁 Script executed:
#!/bin/bash
# Verify that the current CORS configuration in backend/src/config/corsConfig.ts contains the hardcoded origin.
grep -R "origin:" backend/src/config/corsConfig.ts
Length of output: 207
Action Required: Update CORS Configuration to Use Environment Variables
-
In backend/src/config/corsConfig.ts (lines 4-9), the
origin
is hardcoded as'http://localhost:3000'
, which limits its use to a development environment. -
To support multiple environments, update the configuration to source the allowed origins from an environment variable. This enables flexibility for development, staging, and production settings.
-
Recommended Diff:
export const corsConfig: CorsOptions = { - origin: 'http://localhost:3000', + origin: process.env.ALLOWED_ORIGINS?.split(',') || 'http://localhost:3000', methods: ['GET', 'POST', 'PUT', 'DELETE'], allowedHeaders: ['Content-Type', 'Authorization'], credentials: true, };
-
Next Steps:
Ensure your environment configuration (and any relevant documentation) includes the newALLOWED_ORIGINS
variable to properly manage allowed origins across different deployment stages.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
export const corsConfig: CorsOptions = { | |
origin: 'http://localhost:3000', | |
methods: ['GET', 'POST', 'PUT', 'DELETE'], | |
allowedHeaders: ['Content-Type', 'Authorization'], | |
credentials: true, | |
}; | |
export const corsConfig: CorsOptions = { | |
origin: process.env.ALLOWED_ORIGINS?.split(',') || 'http://localhost:3000', | |
methods: ['GET', 'POST', 'PUT', 'DELETE'], | |
allowedHeaders: ['Content-Type', 'Authorization'], | |
credentials: true, | |
}; |
export interface ISearchProps { | ||
placeholder: string; | ||
value: string; | ||
onChange: () => void; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Fix the onChange handler type signature
The onChange
function doesn't accept any parameters, which is unusual for a search component. Typically, an onChange handler in React should receive either an event parameter or at least the new input value to update state.
export interface ISearchProps {
placeholder: string;
value: string;
- onChange: () => void;
+ onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
}
Alternatively, if you prefer passing just the value:
export interface ISearchProps {
placeholder: string;
value: string;
- onChange: () => void;
+ onChange: (value: string) => void;
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
export interface ISearchProps { | |
placeholder: string; | |
value: string; | |
onChange: () => void; | |
} | |
export interface ISearchProps { | |
placeholder: string; | |
value: string; | |
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void; | |
} |
export interface ISearchProps { | |
placeholder: string; | |
value: string; | |
onChange: () => void; | |
} | |
export interface ISearchProps { | |
placeholder: string; | |
value: string; | |
onChange: (value: string) => void; | |
} |
} | ||
|
||
@Delete(':id') | ||
remove(@Param('id') id: string | number): Promise<void> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Inconsistent parameter type in the remove method.
The id
parameter in the remove
method accepts string | number
while other methods only accept string
. This is inconsistent with the rest of the controller and could lead to type conversion issues.
For consistency, use the same type for the id
parameter across all methods:
- remove(@Param('id') id: string | number): Promise<void> {
+ remove(@Param('id') id: string): Promise<void> {
return this.todoService.remove(id);
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
remove(@Param('id') id: string | number): Promise<void> { | |
remove(@Param('id') id: string): Promise<void> { | |
return this.todoService.remove(id); | |
} |
@Get(':id') | ||
findOne(@Param('id') id: string): Promise<Todo> { | ||
return this.todoService.findOne(id); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add error handling for findOne method.
The findOne
method doesn't handle cases where a todo with the specified ID doesn't exist, which could result in unhandled exceptions.
Consider adding error handling:
@Get(':id')
findOne(@Param('id') id: string): Promise<Todo> {
- return this.todoService.findOne(id);
+ try {
+ return this.todoService.findOne(id);
+ } catch (error) {
+ throw new NotFoundException(`Todo with ID '${id}' not found`);
+ }
}
Don't forget to import NotFoundException
from @nestjs/common
.
Committable suggestion skipped: line range outside the PR's diff.
async remove(id: string | number): Promise<void> { | ||
// Obter todos os todos | ||
const todos = await this.todoModel.find().exec(); | ||
|
||
// Encontrar o todo com id correspondente | ||
const todoToDelete = todos.find(todo => { | ||
const numericId = IdFormatter.toNumber(todo._id); | ||
return numericId.toString() === id.toString(); | ||
}); | ||
|
||
if (!todoToDelete) { | ||
throw new NotFoundException(`Todo with ID ${id} not found`); | ||
} | ||
|
||
// Excluir o todo encontrado | ||
await this.todoModel.findByIdAndDelete(todoToDelete._id).exec(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Optimize the remove method to avoid loading all Todos.
Similar to update()
, this method can skip fetching all documents by directly using an ObjectId-based query. It’s more efficient and avoids potential race conditions.
async remove(id: string | number): Promise<void> {
- // Obter todos os todos
- const todos = await this.todoModel.find().exec();
-
- // Encontrar o todo com id correspondente
- const todoToDelete = todos.find(todo => {
- const numericId = IdFormatter.toNumber(todo._id);
- return numericId.toString() === id.toString();
- });
-
- if (!todoToDelete) {
- throw new NotFoundException(\`Todo with ID \${id} not found\`);
- }
-
- // Excluir o todo encontrado
- await this.todoModel.findByIdAndDelete(todoToDelete._id).exec();
+ const objectId = IdFormatter.toObjectId(id);
+ const deletedTodo = await this.todoModel.findByIdAndDelete(objectId).exec();
+ if (!deletedTodo) {
+ throw new NotFoundException(\`Todo with ID \${id} not found\`);
+ }
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
async remove(id: string | number): Promise<void> { | |
// Obter todos os todos | |
const todos = await this.todoModel.find().exec(); | |
// Encontrar o todo com id correspondente | |
const todoToDelete = todos.find(todo => { | |
const numericId = IdFormatter.toNumber(todo._id); | |
return numericId.toString() === id.toString(); | |
}); | |
if (!todoToDelete) { | |
throw new NotFoundException(`Todo with ID ${id} not found`); | |
} | |
// Excluir o todo encontrado | |
await this.todoModel.findByIdAndDelete(todoToDelete._id).exec(); | |
} | |
async remove(id: string | number): Promise<void> { | |
const objectId = IdFormatter.toObjectId(id); | |
const deletedTodo = await this.todoModel.findByIdAndDelete(objectId).exec(); | |
if (!deletedTodo) { | |
throw new NotFoundException(`Todo with ID ${id} not found`); | |
} | |
} |
async update(id: string | number, updateTodoDto: UpdateTodoDto): Promise<Todo> { | ||
// Obter todos os todos | ||
const todos = await this.todoModel.find().exec(); | ||
|
||
// Encontrar o todo com id correspondente | ||
const todoToUpdate = todos.find(todo => { | ||
const numericId = IdFormatter.toNumber(todo._id); | ||
return numericId.toString() === id.toString(); | ||
}); | ||
|
||
if (!todoToUpdate) { | ||
throw new NotFoundException(`Todo with ID ${id} not found`); | ||
} | ||
|
||
// Atualizar o todo encontrado | ||
const updatedTodo = await this.todoModel | ||
.findByIdAndUpdate(todoToUpdate._id, updateTodoDto, { new: true }) | ||
.exec(); | ||
|
||
// Converter o _id do MongoDB para um número para compatibilidade com o frontend | ||
const todoObj = updatedTodo.toObject(); | ||
todoObj.id = IdFormatter.toNumber(updatedTodo._id); | ||
|
||
return todoObj; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Optimize the update method to avoid loading all Todos.
Fetching all Todos and then filtering in memory can be inefficient and prone to race conditions. Directly convert the provided id
to an ObjectId and use Mongoose queries to locate, update, and return the document. Also, handle the scenario where findByIdAndUpdate
returns null
.
async update(id: string | number, updateTodoDto: UpdateTodoDto): Promise<Todo> {
- // Obter todos os todos
- const todos = await this.todoModel.find().exec();
-
- // Encontrar o todo com id correspondente
- const todoToUpdate = todos.find(todo => {
- const numericId = IdFormatter.toNumber(todo._id);
- return numericId.toString() === id.toString();
- });
-
- if (!todoToUpdate) {
- throw new NotFoundException(\`Todo with ID \${id} not found\`);
- }
-
- // Atualizar o todo encontrado
- const updatedTodo = await this.todoModel
- .findByIdAndUpdate(todoToUpdate._id, updateTodoDto, { new: true })
- .exec();
+ const objectId = IdFormatter.toObjectId(id);
+ const updatedTodo = await this.todoModel.findByIdAndUpdate(objectId, updateTodoDto, {
+ new: true,
+ }).exec();
if (!updatedTodo) {
throw new NotFoundException(\`Todo with ID \${id} not found\`);
}
const todoObj = updatedTodo.toObject();
todoObj.id = IdFormatter.toNumber(updatedTodo._id);
return todoObj;
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
async update(id: string | number, updateTodoDto: UpdateTodoDto): Promise<Todo> { | |
// Obter todos os todos | |
const todos = await this.todoModel.find().exec(); | |
// Encontrar o todo com id correspondente | |
const todoToUpdate = todos.find(todo => { | |
const numericId = IdFormatter.toNumber(todo._id); | |
return numericId.toString() === id.toString(); | |
}); | |
if (!todoToUpdate) { | |
throw new NotFoundException(`Todo with ID ${id} not found`); | |
} | |
// Atualizar o todo encontrado | |
const updatedTodo = await this.todoModel | |
.findByIdAndUpdate(todoToUpdate._id, updateTodoDto, { new: true }) | |
.exec(); | |
// Converter o _id do MongoDB para um número para compatibilidade com o frontend | |
const todoObj = updatedTodo.toObject(); | |
todoObj.id = IdFormatter.toNumber(updatedTodo._id); | |
return todoObj; | |
} | |
async update(id: string | number, updateTodoDto: UpdateTodoDto): Promise<Todo> { | |
const objectId = IdFormatter.toObjectId(id); | |
const updatedTodo = await this.todoModel.findByIdAndUpdate(objectId, updateTodoDto, { | |
new: true, | |
}).exec(); | |
if (!updatedTodo) { | |
throw new NotFoundException(`Todo with ID ${id} not found`); | |
} | |
const todoObj = updatedTodo.toObject(); | |
todoObj.id = IdFormatter.toNumber(updatedTodo._id); | |
return todoObj; | |
} |
async findOne(id: string | number): Promise<Todo> { | ||
const objectId = IdFormatter.toObjectId(id); | ||
const todo = await this.todoModel.findById(objectId).exec(); | ||
|
||
if (!todo) { | ||
throw new NotFoundException(`Todo with ID ${id} not found`); | ||
} | ||
|
||
// Converter o _id do MongoDB para um número para compatibilidade com o frontend | ||
const todoObj = todo.toObject(); | ||
todoObj.id = IdFormatter.toNumber(todo._id); | ||
|
||
return todoObj; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Verify that invalid IDs are handled.
When converting the id
to an ObjectId, ensure that IdFormatter.toObjectId()
properly handles invalid strings. Otherwise, the app could throw unhandled cast errors instead of custom exceptions.
Please confirm the behavior of IdFormatter.toObjectId()
for invalid inputs by searching for its definition in the codebase:
If it doesn’t handle invalid inputs, consider adding a defensive check or throwing a descriptive error.
🏁 Script executed:
#!/bin/bash
fd "id-formatter.ts" --exec cat {} \
| rg "toObjectId"
Length of output: 111
I'll run a script to print more lines of the toObjectId
method so we can verify whether it handles invalid inputs:
🏁 Script executed:
#!/bin/bash
fd "id-formatter.ts" --exec cat {} \; | rg "static toObjectId" -A 15
Length of output: 702
Action: Update Invalid ID Handling in IdFormatter.toObjectId
After reviewing the code, when an invalid ID string is provided, the current implementation of toObjectId
falls back to returning a default ObjectId (123400000000000000000000
) instead of throwing a descriptive error. This means that rather than immediately flagging an invalid input, the service proceeds with a lookup and ultimately throws a generic NotFoundException
when no Todo is found. This fallback behavior can obscure the root cause of issues when invalid IDs are passed.
- Location:
backend/src/modules/todos/services/todo.service.ts
(line 38-51) andid-formatter.ts
in thetoObjectId
method. - Issue: Invalid IDs do not trigger a custom error but are silently converted to a default ObjectId.
- Suggestion: Revise
IdFormatter.toObjectId()
to either throw a clear, descriptive exception for invalid inputs or document the fallback behavior explicitly. This change will help ensure that callers receive immediate feedback about the invalid input rather than a misleading "not found" error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (7)
frontend/src/types/ButtonTypes.tsx (4)
1-1
: Consider removing the eslint-disable comment.Starting a file with a linter disable comment is generally not recommended. Instead of disabling Prettier, fix the formatting issues to comply with the project's style guidelines.
2-5
: Consider enhancing the ButtonProps interface for better flexibility.The current interface is minimal but could benefit from additional common button properties like
type
,disabled
,className
, etc. This would make the Button component more reusable across the application.export interface ButtonProps { onClick: () => void; text: string; + type?: 'button' | 'submit' | 'reset'; + disabled?: boolean; + className?: string; }
1-6
: Use .ts extension for type definition files.Since this file only contains TypeScript type definitions and no JSX, it should use the
.ts
extension instead of.tsx
.
2-6
: Fix indentation inconsistency.There's inconsistent indentation in the interface definition. The closing brace has extra indentation.
export interface ButtonProps { onClick: () => void; text: string; - } +}frontend/src/components/Button/index.tsx (3)
1-1
: Consider removing the eslint-disable comment.Instead of disabling Prettier with a comment, fix the formatting issues to comply with the project's style guidelines.
5-7
: Use destructuring for props in function parameters.Destructuring props in the function signature is a common React pattern that improves readability.
- const Button = (props: ButtonProps) => { - return <button onClick={props.onClick}>{props.text}</button>; + const Button = ({ onClick, text }: ButtonProps) => { + return <button onClick={onClick}>{text}</button>;
5-7
: Add styling for the button component.The button currently lacks any styling. Consider adding CSS or using a styling framework to make it visually appealing and consistent with your application's design system.
- const Button = (props: ButtonProps) => { - return <button onClick={props.onClick}>{props.text}</button>; + const Button = (props: ButtonProps) => { + return ( + <button + className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors" + onClick={props.onClick} + > + {props.text} + </button> + );
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
frontend/src/components/Button/index.tsx
(1 hunks)frontend/src/types/ButtonTypes.tsx
(1 hunks)
const Button = (props: ButtonProps) => { | ||
return <button onClick={props.onClick}>{props.text}</button>; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Enhance the Button component with additional functionality and accessibility features.
The current implementation is basic and could benefit from accessibility improvements and additional customization options.
- const Button = (props: ButtonProps) => {
- return <button onClick={props.onClick}>{props.text}</button>;
- };
+ const Button = ({ onClick, text, type = 'button', disabled = false, className = '' }: ButtonProps) => {
+ return (
+ <button
+ onClick={onClick}
+ type={type}
+ disabled={disabled}
+ className={`button ${className}`}
+ aria-label={text}
+ >
+ {text}
+ </button>
+ );
+ };
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
const Button = (props: ButtonProps) => { | |
return <button onClick={props.onClick}>{props.text}</button>; | |
}; | |
const Button = ({ onClick, text, type = 'button', disabled = false, className = '' }: ButtonProps) => { | |
return ( | |
<button | |
onClick={onClick} | |
type={type} | |
disabled={disabled} | |
className={`button ${className}`} | |
aria-label={text} | |
> | |
{text} | |
</button> | |
); | |
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (2)
frontend/src/lib/api.ts (2)
3-7
: Use environment variables for API URL configuration.Hardcoding the API URL to "http://localhost:3003" makes deployment to different environments challenging. Consider using environment variables for configuration.
-const API = "http://localhost:3003"; +const API = process.env.REACT_APP_API_URL || "http://localhost:3003"; const api = axios.create({ baseURL: API, });
11-29
: Add error handling and improve type safety.The API functions lack error handling and use generic
any
types. Consider adding try/catch blocks and using more specific types.-export const get = async (path: string): Promise<any> => { +export const get = async <T = any>(path: string): Promise<T> => { + try { const response = await api.get(path); return response.data; + } catch (error) { + console.error(`GET request failed for ${path}:`, error); + throw error; + } }; -export const post = async (path: string, data: any): Promise<any> => { +export const post = async <T = any>(path: string, data: unknown): Promise<T> => { + try { const response = await api.post(path, data); return response.data; + } catch (error) { + console.error(`POST request failed for ${path}:`, error); + throw error; + } }; // Apply similar changes to put and del methods
🧹 Nitpick comments (2)
frontend/src/lib/api.ts (2)
9-10
: Remove or improve the unused endpoint function.The
endpoint
function is defined but never used within this file. Additionally, it directly concatenates the API URL and path without properly handling path separators, which could lead to malformed URLs.-const endpoint = (path: string): string => API + path;
If you need this function, consider using proper URL joining:
const endpoint = (path: string): string => { return path.startsWith('/') ? API + path : `${API}/${path}`; };
31-31
: Consider adding axios interceptors for global error handling.Since you're exporting the axios instance, you could add interceptors to handle errors globally:
const api = axios.create({ baseURL: API, }); +// Add request interceptor +api.interceptors.request.use( + (config) => { + // You can add auth tokens or other headers here + return config; + }, + (error) => { + return Promise.reject(error); + } +); + +// Add response interceptor +api.interceptors.response.use( + (response) => { + return response; + }, + (error) => { + // Handle common errors (401, 403, 500, etc.) + if (error.response) { + // Handle specific status codes + if (error.response.status === 401) { + // Handle unauthorized + } + } + return Promise.reject(error); + } +); export { api };
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
backend/src/main.ts
(1 hunks)frontend/src/components/DeleteModal/index.tsx
(1 hunks)frontend/src/lib/api.ts
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- backend/src/main.ts
- frontend/src/components/DeleteModal/index.tsx
Olá equipe CoreLab,
Estou submetendo minha solução para o desafio técnico. Desenvolvi uma aplicação completa de gerenciamento de tarefas, incluindo todas as funcionalidades solicitadas e alguns extras.
Sobre a implementação
Optei por desenvolver um projeto integrado com frontend e backend juntos, facilitando a configuração e execução do ambiente completo. Enviei o mesmo código para ambos os repositórios (frontend e backend) para facilitar a avaliação.
Funcionalidades implementadas
CRUD completo de tarefas
Marcação de tarefas como favoritas
Definição de cores personalizadas para tarefas
Interface responsiva seguindo o mockup fornecido
Filtros por cor e favoritos
Pesquisa por texto
Tema claro/escuro
Extras implementados
Tipagens e interfaces com TypeScript
Configuração de ESLint e Prettier
Docker e Docker Compose
Testes unitários e de integração
CI/CD com GitHub Actions
Todos os detalhes da implementação estão no arquivo PULL_REQUEST.md incluído no projeto.
Agradeço a oportunidade e estou à disposição para qualquer esclarecimento.
Atenciosamente, Neil Lima
Summary by CodeRabbit