Skip to content

picture export#174

Merged
mozsarmate merged 5 commits intomainfrom
picture-export
Feb 23, 2026
Merged

picture export#174
mozsarmate merged 5 commits intomainfrom
picture-export

Conversation

@mozsarmate
Copy link
Contributor

No description provided.

@vercel
Copy link

vercel bot commented Feb 23, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
schbody-frontend Ready Ready Preview, Comment Feb 23, 2026 10:37am

Request Review

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR implements a profile picture export feature, allowing administrators to download user profile pictures as a ZIP file. The changes affect both frontend and backend components, adding new API endpoints, UI pages, and export functionality.

Changes:

  • Added backend endpoint and service method to export profile pictures as a ZIP archive using the archiver library
  • Created a dedicated frontend admin page for selecting and exporting specific user profile pictures
  • Integrated profile picture export into existing admin interfaces (roles page and period applications table)
  • Changed ProfilePictureStatus enum values from Hungarian descriptions to English constants for consistency with the backend

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
apps/frontend/src/types/user-entity.ts Changed ProfilePictureStatus enum from Hungarian text values to English constants (ACCEPTED, PENDING, REJECTED)
apps/frontend/src/middleware.ts Added /admin route and subpaths to authentication middleware protection
apps/frontend/src/lib/profile-pictures.ts New utility function for triggering profile picture export API call and file download
apps/frontend/src/components/ui/generating-dialog.tsx Removed unused DialogTitle import and element
apps/frontend/src/components/ui/UserCard.tsx Added mutateUsers prop, authSchId display, improved alt text, and visual indicators for profile picture status
apps/frontend/src/app/roles/page.tsx Added export all profile pictures button and mutateUsers callback
apps/frontend/src/app/periods/[id]/page.tsx Added profile picture export handler for selected applications
apps/frontend/src/app/periods/[id]/data-table.tsx Added menu item to export profile pictures for selected applications
apps/frontend/src/app/admin/profile-picture-export/page.tsx New admin page with checkbox-based UI for selective profile picture export
apps/backend/src/user/user.service.ts Implemented exportProfilePictures method using archiver to create ZIP files
apps/backend/src/user/user.controller.ts Added POST endpoint for profile picture export with proper headers; removed Query import
apps/backend/package.json Added archiver dependency and @types/archiver dev dependency
.idea/jsLinters/eslint.xml Updated ESLint configuration path
Files not reviewed (1)
  • .idea/jsLinters/eslint.xml: Language not supported
Comments suppressed due to low confidence (1)

apps/backend/src/user/user.controller.ts:16

  • The Query decorator is being used on lines 39, 80, 85, and 86, but it's not imported. The removal of the Query import from the @nestjs/common imports will cause a compilation error. Add Query back to the import statement on line 3.
import {
  Body,
  Controller,
  Delete,
  Get,
  Header,
  Param,
  ParseIntPipe,
  Patch,
  Post,
  StreamableFile,
  UploadedFile,
  UseGuards,
  UseInterceptors,
} from '@nestjs/common';

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

import { LuDownload } from 'react-icons/lu';

export default function Page() {
const router = useRouter();
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

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

The router constant is declared but never used in the component. This is an unused variable that should be removed to keep the code clean.

Copilot uses AI. Check for mistakes.
Comment on lines +9 to +11
ACCEPTED = 'ACCEPTED',
PENDING = 'PENDING',
REJECTED = 'REJECTED',
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

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

The ProfilePictureStatus enum values have been changed from Hungarian descriptions ('Jóváhagyott profilkép', 'Elbírálás alatt álló profilkép', 'Elutasított profilkép') to English constants ('ACCEPTED', 'PENDING', 'REJECTED'). This change affects the tooltip display in PfpStatusBadge.tsx (line 35), which directly shows the enum value. The tooltip will now display English text like "ACCEPTED" instead of the previous Hungarian descriptions. If the UI should remain in Hungarian, consider adding a separate mapping function to translate these enum values for display purposes.

Suggested change
ACCEPTED = 'ACCEPTED',
PENDING = 'PENDING',
REJECTED = 'REJECTED',
ACCEPTED = 'Jóváhagyott profilkép',
PENDING = 'Elbírálás alatt álló profilkép',
REJECTED = 'Elutasított profilkép',

Copilot uses AI. Check for mistakes.
});

return new Promise((resolve, reject) => {
const archive = archiver('zip', { zlib: { level: 0 } });
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

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

The archiver is configured with compression level 0 (zlib: { level: 0 }), which means no compression. This will result in larger ZIP files. While this may be intentional for faster processing, consider using a moderate compression level (e.g., level 6) to balance file size and performance, especially if many profile pictures are being exported.

Suggested change
const archive = archiver('zip', { zlib: { level: 0 } });
const archive = archiver('zip', { zlib: { level: 6 } });

Copilot uses AI. Check for mistakes.

for (const picture of pictures) {
const imageBuffer = Buffer.from(picture.profileImage.buffer);
const safeName = picture.user.fullName.replace(/[^\w\s\-áéíóöőúüűÁÉÍÓÖŐÚÜŰ]/g, '_');
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

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

The safeName sanitization removes potentially problematic characters, but it doesn't handle the case where the fullName might be empty or only contain special characters, which would result in an empty filename. Consider adding a fallback to use authSchId if the sanitized name is empty, for example: const safeName = picture.user.fullName.replace(/[^\w\s\-áéíóöőúüűÁÉÍÓÖŐÚÜŰ]/g, '_').trim() || picture.user.authSchId;

Suggested change
const safeName = picture.user.fullName.replace(/[^\w\s\-áéíóöőúüűÁÉÍÓÖŐÚÜŰ]/g, '_');
const safeName =
picture.user.fullName.replace(/[^\w\s\-áéíóöőúüűÁÉÍÓÖŐÚÜŰ]/g, '_').trim() ||
picture.user.authSchId;

Copilot uses AI. Check for mistakes.
await props.mutateUsers();
}

// @ts-ignore
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

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

The @ts-ignore comment is suppressing TypeScript errors without explanation. This should be removed and the underlying TypeScript error should be properly fixed. If there's a legitimate reason to suppress the error, it should be documented with a comment explaining why.

Suggested change
// @ts-ignore

Copilot uses AI. Check for mistakes.
for (const picture of pictures) {
const imageBuffer = Buffer.from(picture.profileImage.buffer);
const safeName = picture.user.fullName.replace(/[^\w\s\-áéíóöőúüűÁÉÍÓÖŐÚÜŰ]/g, '_');
archive.append(imageBuffer, { name: `${safeName}.jpg` });
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

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

Multiple users with the same fullName will have filename collisions in the exported ZIP file. When two users have identical names, only one file will be preserved in the archive. Consider including the authSchId in the filename to ensure uniqueness, for example: ${safeName}_${picture.user.authSchId}.jpg

Suggested change
archive.append(imageBuffer, { name: `${safeName}.jpg` });
archive.append(imageBuffer, { name: `${safeName}_${picture.user.authSchId}.jpg` });

Copilot uses AI. Check for mistakes.
Comment on lines 4 to 16
const response = await api.post(
'users/profile-pictures/export',
{ userIds: userIds ?? [] },
{ responseType: 'blob' }
);
const objectUrl = URL.createObjectURL(new Blob([response.data], { type: 'application/zip' }));
const link = document.createElement('a');
link.href = objectUrl;
link.download = 'profile-pictures.zip';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(objectUrl);
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

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

The exportProfilePictures function lacks error handling. If the API request fails, the error will propagate to the caller without any user-facing feedback. Consider wrapping the API call in a try-catch block and displaying a toast notification on error, consistent with error handling patterns seen elsewhere in the codebase.

Suggested change
const response = await api.post(
'users/profile-pictures/export',
{ userIds: userIds ?? [] },
{ responseType: 'blob' }
);
const objectUrl = URL.createObjectURL(new Blob([response.data], { type: 'application/zip' }));
const link = document.createElement('a');
link.href = objectUrl;
link.download = 'profile-pictures.zip';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(objectUrl);
try {
const response = await api.post(
'users/profile-pictures/export',
{ userIds: userIds ?? [] },
{ responseType: 'blob' }
);
const objectUrl = URL.createObjectURL(new Blob([response.data], { type: 'application/zip' }));
const link = document.createElement('a');
link.href = objectUrl;
link.download = 'profile-pictures.zip';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(objectUrl);
} catch (error) {
// Provide user-facing feedback if the export fails.
window.alert('Failed to export profile pictures. Please try again.');
// Optional: keep logging for debugging purposes.
// eslint-disable-next-line no-console
console.error('Error exporting profile pictures:', error);
throw error;
}

Copilot uses AI. Check for mistakes.
@mozsarmate mozsarmate merged commit 52b6820 into main Feb 23, 2026
5 checks passed
@mozsarmate mozsarmate deleted the picture-export branch February 23, 2026 10:50
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