Skip to content

Conversation

joaquinGam
Copy link

Description

Please include a summary of the changes and the related issue. List any dependencies that are required for this change.

Fixes # (issue)

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update

How Has This Been Tested?

Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce.

  • Test A
  • Test B

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • Any dependent changes have been merged and published in downstream modules
  • I have checked my code and corrected any misspellings

Joaquin added 9 commits September 7, 2025 16:31
	Monorepo Setup:
		Initialized the base project scaffolding and configuration using create-awesome-node-app.
		Fixed dependency errors.
		Removed unnecessary package folders to simplify the demo solution.
		Removed extra configurations like changesets and some Turborepo settings to keep the setup straightforward.

	Frontend:
		Bootstrapped the frontend project with create vite@latest --template react-ts.
		Added shadcn and Tailwind CSS for styling.

	Backend:
		Bootstrapped the backend project with create-awesome-node-app.
		Removed unnecessary files and configurations to simplify the setup.
		Updated fixed versions and made changes to eslint.config.mjs.

	Docker & Compose:
		Added Dockerfiles for development purposes to assist with the review process.
		Added compose.yml for development and review workflows.
	Notes:
		Disabled the example test file app.e2e-spec.ts to avoid unresolved lint errors. This
		   commit focuses on setting up the initial solution
…ment modules

	Removed unused `app.controller` and its test file
	Updated `app.module` to integrate new modules
	Added `campaigns` module:
	  - `campaign.entity.ts` for Campaign entity definition
	  - `campaigns.controller.ts` for Campaigns API endpoints
	  - `campaigns.service.ts` for Campaigns business logic
	  - `create-campaign.dto.ts` for Campaign creation validation
	Added `content-pieces` module:
	  - `content-piece.entity.ts` for ContentPiece entity definition
	  - `content-pieces.controller.ts` for ContentPieces API endpoints
	  - `content-pieces.service.ts` for ContentPieces business logic
	  - DTOs for create and update operations
	  - `review-state.enum.ts` for review state management
	Added `content-piece-translations` module:
	  - `content-piece-translation.entity.ts` for ContentPieceTranslation entity
	  - `content-piece-translation.controller.ts` for API endpoints
	  - `content-piece-translation.service.ts` for business logic
	  - DTOs for create and update operations
	Updated `eslint.config.mjs` and `package.json` for new configurations
	  - Add required dependencies like typeORM and class-validator
	  - Config eslint to prevent unnecesary error messages
	Updated `main.ts` to reflect new application structure
	GraphQL API: Added GraphQL resolvers for Campaigns, ContentPieces, and ContentPieceTranslations to enable
	    real-time updates.
	Subscriptions: Configured PubSub for GraphQL subscriptions to support live updates in the frontend.
	Modules and Services:
	  - Refactored CampaignsService to include pubSub.publish for event-driven updates.
	  - Added new modules and resolvers for ContentPieceTranslations and ContentPieces.
	Entity Updates:
	  - Updated Campaign, ContentPiece, and ContentPieceTranslation entities to align with the new GraphQL schema.
	File Renames: Renamed files in ContentPieceTranslations for consistency with naming conventions.
	Dependencies: Added required dependencies for GraphQL subscriptions (graphql-subscriptions, @nestjs/graphql, etc.).
	Configuration:
	  - Updated eslint.config.mjs for TypeScript linting.
	  - Updated main.ts to enable WebSocket support for subscriptions.
	  - Removed: Deleted unused ContentPieceTranslationModule.

	Why: These changes enable the backend to support a GraphQL API with real-time subscriptions, allowing
	    the frontend to receive live updates for campaigns and content pieces.
	UI Components:
	  - Added CampaignTable and CampaignRow components for displaying campaign data.
	  - Added ContentTable and ContentRow components for displaying content pieces.
	  - Introduced reusable UI components: table and tooltip.

	GraphQL Integration:
	  - Implemented CampaignContext to manage campaign data and handle real-time updates via GraphQL subscriptions.
	  - Added apolloClient setup for GraphQL queries and subscriptions.

	Pages:
	  - Created CampaignIndex and CampaignEdit pages for campaign management.
	  - Added ContentIndex page for managing content pieces.
	  - Added NotFound page for handling invalid routes.

	Types and Validation:
	  - Defined TypeScript types in lib/types.ts for campaigns, content pieces, and translations.
	  - Added validators.ts for input validation.

	Styling:
	  - Updated index.css for basic styling.
	  - Removed unused App.css and react.svg.

	Configuration:
	  - Updated tsconfig.app.json to include new paths and configurations.
	  - Updated index.html for the new app structure.

	Why: These changes introduce a basic UI and integrate GraphQL subscriptions to enable real-time updates
	    for campaigns and content pieces. This lays the foundation for further development and testing.
This commit introduces significant updates to the backend to support AI-powered content generation and translation workflows. The changes include:

Campaigns Module:
	Updated the campaign.entity.ts to include relationships with content pieces.
	Enhanced the campaigns.controller.ts and campaigns.resolver.ts to support querying campaigns with their content and translations.
	Updated the campaigns.service.ts to handle campaign-related logic more effectively.

Content Pieces:
	Modified the content-piece.entity.ts to establish relationships with translations.
	Added a new generate-content.dto.ts to handle AI generation requests.
	Updated the content-pieces.controller.ts and content-pieces.service.ts to include endpoints and logic for generating AI-powered drafts and translations.
	Integrated LangChain for AI model selection (OpenAI/Anthropic) in langchain.service.ts.

Content Piece Translations:
	Updated the content-piece-translations.entity.ts to support localized translations.
	Enhanced the content-piece-translations.controller.ts and content-piece-translations.service.ts to manage translation entities.
	Added validation and DTOs for creating and updating translations.

LangChain Integration:
	Introduced langchain.enum.ts to define supported AI models (OpenAI, Anthropic).
	Updated langchain.service.ts to handle AI-powered content generation and localization.

These changes enable the backend to:
	Generate AI-powered drafts for content pieces.
	Provide translation/localization suggestions via AI.
	Manage relationships between campaigns, content pieces, and translations.
	Support multi-model AI workflows using LangChain.
…frontend

This commit introduces significant updates to the frontend to support the AI-powered content generation and review workflow. The changes include:

Core Pages:
	Updated ContentIndex.tsx to display campaign-specific content pieces and integrate the AI-powered draft generation feature.
	Updated CampaignIndex.tsx to improve the campaign listing and navigation experience.
	Added CampaignCreate.tsx for creating new campaigns.
	Removed CampaignEdit.tsx as its functionality was merged into other components.

UI Enhancements:
	Added reusable UI components:
		badge.tsx, dialog.tsx, input.tsx, label.tsx, select.tsx, sonner.tsx, and spinner.tsx for consistent and modular UI elements.
	Updated tooltip.tsx for better user guidance.
	Added Header.tsx for a consistent page header across the app.

Content Management:
	Enhanced ContentTable and ContentRow components to display content pieces with their review states and AI generation status.
	Added ReviewDialogForms.tsx to handle review actions (approve/reject) for content pieces.

Campaign Management:
	Improved CampaignTable and CampaignRow components for better campaign visualization and interaction.

Context and State Management:
	Updated CampaignContext.tsx to handle real-time updates for campaigns and content pieces.
	Added GenerationConfigContext.tsx to manage AI generation configurations (locale, model provider).

API Integration:
	Added API utilities:
		campaign.ts, contentPiece.ts, contentPieceTranslation.ts, and util.ts for interacting with the backend.
	Centralized configuration in config.ts and error handling in errors.ts.

Styling and Assets:
	Updated index.css for consistent styling across the app.
	Added a new favicon (favicon.png) for branding.

Miscellaneous:
	Updated index.html and package.json to reflect the new dependencies and configurations.
@pedrobrost
Copy link

Hola @joaquinGam, gracias por el PR del challenge técnico!

Estuve probando la solución y cubre muy bien los requisitos, se pueden crear campañas y luego con AI generar las content pieces con sus traducciones.

También revisé la implementación, de la cual me gustó:

  • La sencillez para levantar el entorno con Docker.
  • Si bien el setup local corre con Docker, me gustó la elección de Turborepo para el setup del monorepo.
  • El uso de LangChain para lo de AI.
  • Las actualizaciones en tiempo real funcionan bien con graphql-ws y Apollo.
  • La API está documentada con Swagger.
  • Uso de Tailwindcss y shadcn

Te dejo también algunas observaciones sobre cosas que quizás se podrían mejorar, y preguntas sobre la solución.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Me gusta cómo está organizada la documentación, pero quizás hubiese estado mejor eliminar o modificar un poco los otros archivos que quedaron por defecto del template de Turborepo y otras herramientas.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gracias. Actualice un poco las referencias pero quise dejar documentación en referencia al uso del template y que puedan ver que lo use


if (existingTranslation) {
// Update existing translation
const generatedData = await this.langChainService.generateDraft(locale, 'random', modelProvider);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noté que al generar un content piece con AI, el contenido no tenía nada que ver con el nombre y descripción que le daba a la campaña, lo cual entiendo que es por estos random que quedaron acá.

Supongo que es algo que quedó viejo, porque tenés la data acá nomás para pasarla y que genere algo relacionado.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claro! Al comienzo la idea era hacer un input en algún formulario, pero pense que quiza seria mejor simplemente utilizar la información de la campaña. Para testear rapido puse el 'random' y luego no lo actualice. Pero si, el poder recibir un parámetro era la idea

Comment on lines 75 to 85
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Me llamó un poco la atención que si bien usaste componentes de shadcn y tailwind para los estilos en general, para algunas cosas usaste css como por ejemplo acá en los botones, hubo alguna razón para eso?

Otra cosa relacionada con los botones es que tienen un problema de contraste, teniendo el background color blanco y el texto también de un tono muy claro, casi no se llegan a leer.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

El tema del contraste y el css ocasional fue por lo siguiente: el css quedo viejo y bueno, no me dio tiempo a hacer la revisión de si me había quedado en algún lado.
El tema del contraste es porque al comienzo quería hacer light/dark, para simplificarlo solo implemente uno pero, luego de ya haber subido todo me percate que mi navegador fuerza el modo oscuro. Así que, el problema de accesibilidad viene por de ahí


export async function create(data: Partial<Campaign>) {
try {
const response = api.post<Campaign, Partial<Campaign>>('/campaigns', data);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

faltaron unos await en el create y update

Comment on lines 69 to 71
<Link to={`/campaigns/${campaignId}/content/new`} className="text-white underline flex flex-row">
<Plus className="mr-2" /> Create
</Link>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Este boton lleva a una ruta que no existe, deberia hacer algo?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

En un comienzo, iba a dejar que el usuario pueda crear su propio contenido a mano, pero luego lo descarte. El botón quedó viejo

const result = await chain.invoke({
topic,
sourceLanguage,
json_example: JSON.stringify({ title: 'Exciting Title', description: 'Engaging description text.' }),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Si bien pasas un json_example, el prompt no lo agrega en ningun lado

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Al comienzo le brindaba un ejemplo del json que quería, luego lo removi ya que cambie el prompt. También quedo viejo el input que se envía

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants