Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

File Archiving, Perms, Card Layout #10384

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from

Conversation

Jacobjeevan
Copy link
Contributor

@Jacobjeevan Jacobjeevan commented Feb 4, 2025

Proposed Changes

Archived

Screenshot 2025-02-04 at 2 01 46 PM Screenshot 2025-02-04 at 2 02 15 PM

Switched to card layout for screens <= 1080px.

Screenshot 2025-02-04 at 2 25 22 PM Screenshot 2025-02-04 at 2 25 28 PM

Hide upload and edit buttons

  • encounters with completed/cancelled/entered_in_error/discontinued statuses (matching BE)
  • users with no edit encounter permissions
  • users with no edit patient permissions.
Screenshot 2025-02-04 at 2 03 00 PM

@ohcnetwork/care-fe-code-reviewers

Merge Checklist

  • Add specs that demonstrate bug / test a new feature.
  • Update product documentation.
  • Ensure that UI text is kept in I18n files.
  • Prep screenshot or demo video for changelog entry, and attach it to issue.
  • Request for Peer Reviews
  • Completion of QA

Summary by CodeRabbit

  • New Features
    • Added localized messages for archiving details, including information on when items were archived, who archived them, the archived files, and the reason for archiving.
    • Introduced new dialog interfaces to display archived file details, play audio files, and streamline file uploads with progress indicators and file management options.
    • Enhanced the file management view with improved layouts and permission checks, now leveraging complete encounter and patient data for more comprehensive file detail presentations.

@Jacobjeevan Jacobjeevan requested a review from a team as a code owner February 4, 2025 09:21
Copy link
Contributor

coderabbitai bot commented Feb 4, 2025

Walkthrough

This update introduces several new keys in the localization file to support archiving details. Multiple new dialog components are added, including those for displaying archived file details, playing audio files, and managing file uploads. The FilesTab component is updated to pass complete encounter and patient objects instead of simple IDs, and it now includes permission checks and dynamic UI renderings. Updates in associated components propagate the changes to ensure proper data flow and user interactions across the file management features.

Changes

File(s) Change Summary
public/locale/en.json Added new keys: "archived_at", "archived_by", "archived_file", "archived_reason" to support archiving functionalities.
src/components/Files/ArchivedFileDialog.tsx, src/components/Files/AudioPlayerDialog.tsx, src/components/Files/FileUploadDialog.tsx Introduced three new dialog components for displaying archived file details, managing audio playback, and handling file uploads; each accepts relevant props and leverages internationalization and UI libraries.
src/components/Files/FilesTab.tsx Updated prop types by replacing encounterId with encounter and adding an optional patient prop; added permission checks and refactored rendering logic for enhanced file management.
src/components/Patient/PatientDetailsTab/PatientFiles.tsx Passed complete patient data to the FilesTab component via a new patient prop.
src/pages/Encounters/tabs/EncounterFilesTab.tsx Replaced the encounterId prop with the full encounter object when rendering the FilesTab component.

Sequence Diagram(s)

sequenceDiagram
    participant U as User
    participant FT as FilesTab
    participant AFD as ArchivedFileDialog

    U->>FT: Click "View Archived File" button
    FT->>AFD: Open dialog with file details
    AFD->>AFD: Format archive date (using dayjs) and fetch translations
    AFD->>U: Display archived info (reason, archived_by, archived_at)
    U->>AFD: Close dialog
    AFD->>FT: Trigger onOpenChange callback to close dialog
Loading
sequenceDiagram
    participant U as User
    participant FT as FilesTab
    participant APD as AudioPlayerDialog
    participant Q as Query (react-query)
    participant AP as AudioPlayer

    U->>FT: Click "Play Audio" button
    FT->>APD: Open AudioPlayerDialog with file info
    APD->>Q: Fetch signed audio URL (if file is valid)
    Q->>APD: Return signed URL
    APD->>AP: Render audio player with URL
    U->>APD: Close dialog, stopping playback
Loading
sequenceDiagram
    participant U as User
    participant FT as FilesTab
    participant FUD as FileUploadDialog
    participant FU as FileUpload Logic

    U->>FT: Click "Upload File" button
    FT->>FUD: Open FileUploadDialog with file upload object
    FUD->>U: Display file list, input fields, and progress indicators
    U->>FUD: Modify file name / remove file / start upload
    FUD->>FU: Execute corresponding file upload operations
    FU->>FUD: Update progress, error, or completion status
    U->>FUD: Close dialog
Loading

Assessment against linked issues

Objective Addressed Explanation
Add support to view file archive reason and user details (#10255)

Suggested labels

needs review, tested, changes required

Suggested reviewers

  • rithviknishad

Poem

A hop, a skip, in code so fine,
I’m a bouncy rabbit crossing the line.
Dialogs bloom with details new,
Archiving secrets coming through.
With carrots and code, I celebrate with glee! 🐇💻

Tip

🌐 Web search-backed reviews and chat
  • We have enabled web search-based reviews and chat for all users. This feature allows CodeRabbit to access the latest documentation and information on the web.
  • You can disable this feature by setting web_search: false in the knowledge_base settings.
  • Please share any feedback in the Discord discussion.

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

netlify bot commented Feb 4, 2025

Deploy Preview for care-ohc ready!

Name Link
🔨 Latest commit 691f28d
🔍 Latest deploy log https://app.netlify.com/sites/care-ohc/deploys/67a32c553d502500088dddda
😎 Deploy Preview https://deploy-preview-10384--care-ohc.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

🧹 Nitpick comments (11)
src/components/Files/FilesTab.tsx (5)

56-57: New optional props enhance flexibility.
Adding encounter?: Encounter and patient?: Patient ensures this component can handle multiple data contexts. Consider adding inline documentation for these props if usage grows more complex.


213-229: Permission checks look correct and comprehensive.
Logic to disable actions based on encounter status or user permissions is sound. Consider extracting the list of blocked statuses into a shared constant to make it easier to maintain.


234-302: Detailed control buttons with good user cues.
The conditionals allow granular actions (play, view, download, archive, rename). Overall structure is fine, but if the list of actions grows, consider refactoring into smaller components or utilities to reduce complexity.


417-478: Responsive card layout is well-implemented.
Effectively displays file data for smaller screens. The duplication with table layout is understandable for responsiveness, but consider extracting shared logic (e.g., file info display) into a helper for maintainability.


479-591: Tabular view is user-friendly on larger devices.
Repeats some logic seen in the card layout. Creating a shared subcomponent might help unify file representation and reduce duplication going forward.

src/components/Files/AudioPlayerDialog.tsx (2)

27-34: Add loading and error states for data fetching.

The query should handle loading and error states to provide better user feedback.

-  const { data: fileData } = useQuery({
+  const { data: fileData, isLoading, error } = useQuery({
     queryKey: [routes.retrieveUpload, type, file?.id],
     queryFn: query(routes.retrieveUpload, {
       queryParams: { file_type: type, associating_id: associatingId },
       pathParams: { id: file?.id || "" },
     }),
     enabled: !!file?.id,
   });
+  if (isLoading) return <div>Loading...</div>;
+  if (error) return <div>Error loading audio file</div>;

39-46: Enhance dialog accessibility.

The dialog should have a more descriptive aria-label.

   <Dialog
     open={open}
     onOpenChange={() => {
       stopPlayback();
       onOpenChange(false);
     }}
-    aria-labelledby="audio-player-dialog"
+    aria-labelledby="audio-player-dialog-title"
+    aria-describedby="audio-player-dialog-description"
   >
src/components/Files/ArchivedFileDialog.tsx (2)

22-22: Improve null handling for file name construction.

The current implementation might result in "undefined" being displayed if both name and extension are null.

-  const fileName = file?.name ? file.name + file.extension : "";
+  const fileName = file?.name ? `${file.name}${file.extension || ""}` : "";

50-52: Add date format localization support.

The date format should respect the user's locale settings.

-  {dayjs(file?.archived_datetime).format("DD MMM YYYY, hh:mm A")}
+  {dayjs(file?.archived_datetime).locale(i18next.language).format("L LT")}
src/components/Files/FileUploadDialog.tsx (2)

45-92: Enhance accessibility for file list items.

The file list items should be more accessible with proper ARIA attributes and keyboard navigation.

-  <div key={index} className="space-y-2">
+  <div 
+    key={index} 
+    className="space-y-2"
+    role="listitem"
+    aria-labelledby={`file-name-${index}`}
+  >
     <div className="flex items-center justify-between gap-2 rounded-md bg-secondary-300 px-4 py-2">
-      <span className="flex items-center truncate">
+      <span 
+        className="flex items-center truncate"
+        id={`file-name-${index}`}
+      >

115-117: Add determinate progress indicator.

The progress bar should include textual representation of progress.

   {!!fileUpload.progress && (
-    <Progress value={fileUpload.progress} className="mt-4" />
+    <div className="mt-4">
+      <Progress 
+        value={fileUpload.progress} 
+        aria-label="Upload progress"
+        aria-valuemin={0}
+        aria-valuemax={100}
+        aria-valuenow={fileUpload.progress}
+      />
+      <span className="text-sm text-gray-500 mt-1">
+        {Math.round(fileUpload.progress)}% uploaded
+      </span>
+    </div>
   )}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f2db2ba and deeeb24.

📒 Files selected for processing (7)
  • public/locale/en.json (1 hunks)
  • src/components/Files/ArchivedFileDialog.tsx (1 hunks)
  • src/components/Files/AudioPlayerDialog.tsx (1 hunks)
  • src/components/Files/FileUploadDialog.tsx (1 hunks)
  • src/components/Files/FilesTab.tsx (11 hunks)
  • src/components/Patient/PatientDetailsTab/PatientFiles.tsx (1 hunks)
  • src/pages/Encounters/tabs/EncounterFilesTab.tsx (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: cypress-run (1)
  • GitHub Check: OSSAR-Scan
🔇 Additional comments (12)
src/components/Files/FilesTab.tsx (9)

3-3: Good introduction of i18n.
Importing t from i18next is appropriate and consistent with other i18n usage in the codebase.


34-36: All dialog imports appear valid and properly utilized.
These imports for ArchivedFileDialog, AudioPlayerDialog, and FileUploadDialog align well with the respective components seen later in the code.


40-40: Proper hook usage for file uploads.
Importing useFileUpload seems consistent with the rest of the file’s functionality.


49-50: Well-structured data imports.
Pulling Encounter and Patient types from separate modules improves clarity and modularity.


61-61: Destructured assignment is clean and readable.
Nicely captures incoming props, ensuring clarity on the data being used.


66-68: State variables for handling archived files.
Properly sets local component state to manage dialog openings and selected archived files. No immediate issues.


191-208: Clear UI feedback for archived files.
The getArchivedMessage function and included button elegantly convey archived file status. It’s a nice usability feature that helps differentiate active vs. archived files.


363-363: Straightforward shortcut for hidden upload buttons.
Early return ensures no UI is rendered if editPermission is false. This is concise and clear.


639-639: Ensures card layout is rendered within TabsContent.
Linking <RenderCard /> in this tab context appropriately completes the responsive behavior.

src/components/Patient/PatientDetailsTab/PatientFiles.tsx (1)

11-11: Passing the full patient object is appropriate.
Provides richer data context and aligns with the updated FilesTab signature.

src/pages/Encounters/tabs/EncounterFilesTab.tsx (1)

10-10: Prop change aligns with new FilesTab interface.
Removing encounterId in favor of the entire encounter object aids in more detailed context sharing.

public/locale/en.json (1)

384-388: LGTM! New localization keys are consistent.

The added keys for archiving functionality match their usage in the ArchivedFileDialog component.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

🔭 Outside diff range comments (1)
src/components/Files/FilesTab.tsx (1)

92-110: Consider implementing error handling for the query.

The useQuery hook should include error handling to gracefully handle API failures.

 const {
   data: files,
   isLoading: filesLoading,
+  error,
   refetch,
 } = useQuery({
   queryKey: ["files", type, associatingId, qParams],
   queryFn: query(routes.viewUpload, {
     queryParams: {
       // ... existing params
     },
   }),
 });

+if (error) {
+  return <div className="text-red-500">{t("error_loading_files")}</div>;
+}
🧹 Nitpick comments (4)
src/components/Files/FilesTab.tsx (4)

214-230: Consider extracting status check to a constant.

The status check array could be moved to a constant to improve maintainability and reusability.

+const READONLY_ENCOUNTER_STATUSES = [
+  "completed",
+  "cancelled",
+  "entered_in_error",
+  "discontinued",
+] as const;

 const editPermission = () => {
   if (type === "encounter") {
     return (
       encounter &&
-      ![
-        "completed",
-        "cancelled",
-        "entered_in_error",
-        "discontinued",
-      ].includes(encounter.status) &&
+      !READONLY_ENCOUNTER_STATUSES.includes(encounter.status) &&
       hasPermission("can_write_encounter")
     );
   } else if (type === "patient") {
     return hasPermission("can_write_patient");
   }
   return false;
 };

480-592: Consider extracting common file rendering logic.

The RenderCard and RenderTable components share similar logic for rendering file information. Consider extracting the common parts to reduce code duplication.

+const FileInfo = ({ file, filetype }: { file: FileUploadModel; filetype: string }) => {
+  const fileName = file.name ? file.name + file.extension : "";
+  return (
+    <>
+      <span className="p-2 rounded-full bg-gray-100 shrink-0">
+        <CareIcon icon={icons[filetype]} className="text-xl" />
+      </span>
+      <div className="min-w-0 flex-1">
+        <div className="font-medium text-gray-900 truncate">{fileName}</div>
+        <div className="mt-1 text-sm text-gray-500">{filetype}</div>
+      </div>
+    </>
+  );
+};

107-107: Uncomment or remove commented code.

There's a commented line for file_category filtering. Either remove it or uncomment if it's needed.

-        //file_category: qParams.file_category,
+        file_category: qParams.file_category,

235-305: Consider breaking down DetailButtons into smaller components.

The DetailButtons component is quite large and handles multiple responsibilities. Consider breaking it down into smaller, more focused components for better maintainability.

+const FileActions = ({ file, associatingId }: { file: FileUploadModel; associatingId: string }) => (
+  <DropdownMenu>
+    <DropdownMenuTrigger asChild>
+      <Button variant="secondary">
+        <CareIcon icon="l-ellipsis-h" />
+      </Button>
+    </DropdownMenuTrigger>
+    <DropdownMenuContent align="start">
+      {/* ... existing dropdown items ... */}
+    </DropdownMenuContent>
+  </DropdownMenu>
+);

 const DetailButtons = ({ file }: { file: FileUploadModel }) => {
   const filetype = getFileType(file);
   return (
     <>
       {editPermission() && (
         <div className="flex flex-row gap-2 justify-end">
           {filetype === "AUDIO" && !file.is_archived && <AudioButton file={file} />}
           {fileManager.isPreviewable(file) && <PreviewButton file={file} />}
           <FileActions file={file} associatingId={associatingId} />
         </div>
       )}
     </>
   );
 };
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between deeeb24 and 691f28d.

📒 Files selected for processing (1)
  • src/components/Files/FilesTab.tsx (12 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: Redirect rules - care-ohc
  • GitHub Check: Header rules - care-ohc
  • GitHub Check: Pages changed - care-ohc
  • GitHub Check: Test
  • GitHub Check: OSSAR-Scan
  • GitHub Check: cypress-run (1)
  • GitHub Check: CodeQL-Build
🔇 Additional comments (2)
src/components/Files/FilesTab.tsx (2)

52-58: Props interface update aligns with best practices.

The change from passing IDs to complete objects (Encounter and Patient) improves type safety and reduces the need for additional data fetching.


418-478: Responsive design implementation looks good.

The card layout for smaller screens (xl:hidden) is well-implemented with appropriate spacing and information hierarchy. The implementation aligns with the PR objective of improving the UI for smaller screens.

Comment on lines +612 to +616
<ArchivedFileDialog
open={openArchivedFileDialog}
onOpenChange={setOpenArchivedFileDialog}
file={selectedArchivedFile}
/>
Copy link
Contributor

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 archived file dialog implementation.

The new archived file dialog component has been added, but we should verify its implementation to ensure it handles all edge cases properly.


🏁 Script executed:

#!/bin/bash
# Search for ArchivedFileDialog implementation
ast-grep --pattern 'export default function ArchivedFileDialog({ $$ }) {
  $$$
}'

Length of output: 83


🏁 Script executed:

#!/bin/bash
# First, try to locate files with "ArchivedFileDialog" in their name.
echo "Searching for files with 'ArchivedFileDialog' in filename:"
fd "ArchivedFileDialog" --type f

echo -e "\nSearching for the definition of ArchivedFileDialog in TypeScript files:"
rg "export default .*ArchivedFileDialog" --type ts

# If a file is found, display its contents to verify its implementation details.
files=$(fd "ArchivedFileDialog" --type f)
if [ -z "$files" ]; then
  echo "No file found with name containing 'ArchivedFileDialog'."
else
  for file in $files; do
    echo -e "\n=== Contents of: $file ==="
    cat "$file"
    echo "============================"
  done
fi

Length of output: 2554

ArchivedFileDialog implementation issues detected.

  • The component does not guard against a null file value. When file is null, most fields (e.g., fileName, archive reason, archived by) correctly render as empty, but the call to dayjs(file?.archived_datetime) falls back to dayjs(undefined), which returns the current date. This unintended behavior may mislead users into seeing an incorrect archived timestamp.

@Jacobjeevan Jacobjeevan added waiting for related PR a co-related detail PR is under construction and removed needs testing needs review labels Feb 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
waiting for related PR a co-related detail PR is under construction
Projects
None yet
1 participant