Skip to content

Conversation

@cj-vana
Copy link
Collaborator

@cj-vana cj-vana commented Nov 30, 2025

PR Type

Enhancement, Bug fix


Description

  • Add deprecation notice modal with eventools branding

    • Displays platform transition messaging to users
    • Provides links to eventools beta signup
    • Mentions upcoming data export and self-hosting docs
  • Create useDeprecationNotice hook for SSR-safe localStorage management

    • Centralizes modal state logic across pages
    • Prevents hydration errors with useEffect
  • Integrate modal into Landing and Dashboard pages

    • Shared localStorage key ensures consistent dismissal state
  • Fix StagePlotEditor Temporal Dead Zone bug

    • Reorder useEffect hooks to define collaborationEnabled and broadcast before dependency array reference
    • Resolves blank page error in production builds

Diagram Walkthrough

flowchart LR
  A["useDeprecationNotice Hook"] -->|manages state| B["DeprecationNoticeModal Component"]
  B -->|displays| C["eventools Transition Notice"]
  D["Landing Page"] -->|uses| A
  E["Dashboard Page"] -->|uses| A
  F["StagePlotEditor"] -->|reordered useEffect| G["Fixed TDZ Bug"]
Loading

File Walkthrough

Relevant files
Enhancement
useDeprecationNotice.ts
New SSR-safe deprecation notice hook                                         

apps/web/src/hooks/useDeprecationNotice.ts

  • New custom hook for managing deprecation notice modal state
  • Uses localStorage to persist dismissal status across sessions
  • Implements SSR-safe pattern with useEffect to avoid hydration errors
  • Exports isOpen state and onClose callback for modal integration
+21/-0   
DeprecationNoticeModal.tsx
New deprecation notice modal component                                     

apps/web/src/components/DeprecationNoticeModal.tsx

  • New modal component with eventools branding and logo
  • Displays platform transition messaging with positive tone
  • Includes data export and self-hosting documentation promises
  • Provides beta signup link and dismissal button with Tailwind styling
  • Uses lucide-react icons for visual enhancement
+84/-0   
Dashboard.tsx
Integrate deprecation modal into Dashboard                             

apps/web/src/pages/Dashboard.tsx

  • Import DeprecationNoticeModal component and useDeprecationNotice hook
  • Initialize deprecation notice state in Dashboard component
  • Render modal at bottom of page with state management
  • Maintains existing Dashboard functionality unchanged
+5/-0     
Landing.tsx
Integrate deprecation modal into Landing page                       

apps/web/src/pages/Landing.tsx

  • Import DeprecationNoticeModal component and useDeprecationNotice hook
  • Initialize deprecation notice state in Landing component
  • Render modal at bottom of page with state management
  • Shared localStorage key ensures consistent dismissal across pages
+6/-0     
Bug fix
StagePlotEditor.tsx
Fix StagePlotEditor Temporal Dead Zone bug                             

apps/web/src/pages/StagePlotEditor.tsx

  • Move useEffect hooks that reference collaborationEnabled and broadcast
    variables
  • Reorder hooks to define variables before they are used in dependency
    arrays
  • Fix Temporal Dead Zone error that caused blank page in production
    builds
  • Maintain identical functionality with corrected variable
    initialization order
+37/-37 

cj-vana and others added 2 commits November 30, 2025 07:51
- Add DeprecationNoticeModal component with eventools branding
  - Gentle/positive messaging about platform transition
  - Links to eventools.io for beta signup
  - Mentions upcoming data export and self-hosting docs

- Create useDeprecationNotice hook for SSR-safe localStorage access
  - Centralizes modal logic for Landing and Dashboard pages
  - Uses useEffect to avoid SSR hydration errors

- Integrate modal into Landing and Dashboard pages
  - Shared localStorage key so dismissing on one page dismisses both

- Fix StagePlotEditor TDZ (Temporal Dead Zone) bug
  - Reorder code so collaborationEnabled and broadcast are defined
    before being referenced in useEffect dependency array
  - Fixes blank page error in production builds

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
feat: add deprecation notice modal and fix stage plot blank page bug
@netlify
Copy link

netlify bot commented Nov 30, 2025

Deploy Preview for sounddocsbeta ready!

Name Link
🔨 Latest commit 6d4f33f
🔍 Latest deploy log https://app.netlify.com/projects/sounddocsbeta/deploys/692c5aecdefd3a0008ef4c31
😎 Deploy Preview https://deploy-preview-120--sounddocsbeta.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 project configuration.

@qodo-code-review
Copy link
Contributor

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
Unhandled storage errors

Description: Direct access to localStorage in useEffect and onClose lacks try/catch guards, so in
environments where storage is unavailable or blocked (e.g., Safari ITP, private mode,
restrictive CSP), this can throw and break UI flow, potentially preventing users from
dismissing the modal.
useDeprecationNotice.ts [8-17]

Referred Code
useEffect(() => {
  const dismissed = localStorage.getItem(DEPRECATION_NOTICE_KEY) === "true";
  if (!dismissed) {
    setIsOpen(true);
  }
}, []);

const onClose = () => {
  localStorage.setItem(DEPRECATION_NOTICE_KEY, "true");
  setIsOpen(false);
Insecure external link

Description: External link to https://eventools.io opens in a new tab using target="_blank" without
rel="noreferrer" (only "noopener" present), which can leak the referrer URL to the
external site and enable limited reverse tabnabbing vectors in browsers that ignore
noopener; include both "noopener noreferrer".
DeprecationNoticeModal.tsx [61-69]

Referred Code
<a
  href="https://eventools.io"
  target="_blank"
  rel="noopener noreferrer"
  className="flex-1 inline-flex items-center justify-center px-6 py-3 bg-indigo-600 hover:bg-indigo-500 text-white font-semibold rounded-lg transition-colors"
>
  Sign Up for eventools Beta
  <ExternalLink className="h-4 w-4 ml-2" />
</a>
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
No audit logging: The new deprecation modal display and dismissal actions are not logged, which could be
considered user-facing critical UI state changes needing auditability.

Referred Code
useEffect(() => {
  const dismissed = localStorage.getItem(DEPRECATION_NOTICE_KEY) === "true";
  if (!dismissed) {
    setIsOpen(true);
  }
}, []);

const onClose = () => {
  localStorage.setItem(DEPRECATION_NOTICE_KEY, "true");
  setIsOpen(false);
};

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Missing guards: Access to localStorage is not guarded for SSR or storage unavailability, which may cause
runtime errors without fallback handling.

Referred Code
useEffect(() => {
  const dismissed = localStorage.getItem(DEPRECATION_NOTICE_KEY) === "true";
  if (!dismissed) {
    setIsOpen(true);
  }
}, []);

const onClose = () => {
  localStorage.setItem(DEPRECATION_NOTICE_KEY, "true");
  setIsOpen(false);

Learn more about managing compliance generic rules or creating your own custom rules

Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@cj-vana cj-vana merged commit 93cd437 into main Nov 30, 2025
6 of 9 checks passed
@qodo-code-review
Copy link
Contributor

CI Feedback 🧐

A test triggered by this PR failed. Here is an AI-generated analysis of the failure:

Action: typescript-checks

Failed stage: ESLint on changed files [❌]

Failure summary:

The action failed because ESLint reported errors in the specified changed files, and the lint step
was configured with --max-warnings 0 to fail on any issues.
- File apps/web/src/pages/Dashboard.tsx:
line 92:21 — @typescript-eslint/no-explicit-any
- File apps/web/src/pages/StagePlotEditor.tsx:
-
line 117:82 — @typescript-eslint/no-explicit-any
- line 132:46 —
@typescript-eslint/no-explicit-any
- line 253:31 — @typescript-eslint/no-explicit-any
- line
267:31 — @typescript-eslint/no-explicit-any
- line 277:11 — @typescript-eslint/no-unused-vars
(setEditingField)
- line 296:29 — @typescript-eslint/no-explicit-any
- line 377:56 —
@typescript-eslint/no-explicit-any
- line 379:43 — @typescript-eslint/no-explicit-any
- line
428:18 — @typescript-eslint/no-unused-vars (error)
- line 428:25 —
@typescript-eslint/no-explicit-any
- line 487:18 — @typescript-eslint/no-unused-vars (error)
-
line 730:77 — @typescript-eslint/no-explicit-any
- line 761:47 — @typescript-eslint/no-unused-vars
(icon)
- line 812:21 — @typescript-eslint/no-explicit-any
Summary: 16 problems (15 errors, 1
warning). The script exited with code 1 due to these ESLint errors.

Relevant error logs:
1:  ##[group]Runner Image Provisioner
2:  Hosted Compute Agent
...

176:  + prettier 3.5.3
177:  + typescript 5.9.2
178:  + typescript-eslint 8.39.1
179:  . prepare$ husky
180:  . prepare: Done
181:  Done in 4s using pnpm v9.15.9
182:  ##[group]Run # Convert comma-separated list to space-separated for ESLint
183:  �[36;1m# Convert comma-separated list to space-separated for ESLint�[0m
184:  �[36;1mFILES="apps/web/src/hooks/useDeprecationNotice.ts,apps/web/src/components/DeprecationNoticeModal.tsx,apps/web/src/pages/Dashboard.tsx,apps/web/src/pages/Landing.tsx,apps/web/src/pages/StagePlotEditor.tsx"�[0m
185:  �[36;1mFILES_ARRAY=$(echo "$FILES" | tr ',' ' ')�[0m
186:  �[36;1m�[0m
187:  �[36;1mecho "Running ESLint on changed files:"�[0m
188:  �[36;1mecho "$FILES_ARRAY" | tr ' ' '\n'�[0m
189:  �[36;1m�[0m
190:  �[36;1m# Run ESLint only on changed files�[0m
191:  �[36;1m# Note: ESLint might need context from imported files, but will only report errors in specified files�[0m
192:  �[36;1mnpx eslint $FILES_ARRAY --max-warnings 0 || EXIT_CODE=$?�[0m
193:  �[36;1m�[0m
194:  �[36;1m# If ESLint found issues, fail the check�[0m
195:  �[36;1mif [ "${EXIT_CODE:-0}" -ne 0 ]; then�[0m
...

197:  �[36;1m  exit 1�[0m
198:  �[36;1melse�[0m
199:  �[36;1m  echo "✅ ESLint checks passed"�[0m
200:  �[36;1mfi�[0m
201:  shell: /usr/bin/bash -e {0}
202:  env:
203:  PNPM_HOME: /home/runner/setup-pnpm/node_modules/.bin
204:  ##[endgroup]
205:  Running ESLint on changed files:
206:  apps/web/src/hooks/useDeprecationNotice.ts
207:  apps/web/src/components/DeprecationNoticeModal.tsx
208:  apps/web/src/pages/Dashboard.tsx
209:  apps/web/src/pages/Landing.tsx
210:  apps/web/src/pages/StagePlotEditor.tsx
211:  /home/runner/work/sounddocs/sounddocs/apps/web/src/pages/Dashboard.tsx
212:  ##[error]  92:21  error  Unexpected any. Specify a different type  @typescript-eslint/no-explicit-any
213:  /home/runner/work/sounddocs/sounddocs/apps/web/src/pages/StagePlotEditor.tsx
214:  ##[error]  117:82  error    Unexpected any. Specify a different type                                                                       @typescript-eslint/no-explicit-any
215:  ##[error]  132:46  error    Unexpected any. Specify a different type                                                                       @typescript-eslint/no-explicit-any
216:  ##[error]  253:31  error    Unexpected any. Specify a different type                                                                       @typescript-eslint/no-explicit-any
217:  ##[error]  267:31  error    Unexpected any. Specify a different type                                                                       @typescript-eslint/no-explicit-any
218:  ##[error]  277:11  error    'setEditingField' is assigned a value but never used                                                           @typescript-eslint/no-unused-vars
219:  ##[error]  296:29  error    Unexpected any. Specify a different type                                                                       @typescript-eslint/no-explicit-any
220:  ##[warning]  370:6   warning  React Hook useEffect has a missing dependency: 'handleSave'. Either include it or remove the dependency array  react-hooks/exhaustive-deps
221:  ##[error]  377:56  error    Unexpected any. Specify a different type                                                                       @typescript-eslint/no-explicit-any
222:  ##[error]  379:43  error    Unexpected any. Specify a different type                                                                       @typescript-eslint/no-explicit-any
223:  ##[error]  428:18  error    'error' is defined but never used                                                                              @typescript-eslint/no-unused-vars
224:  ##[error]  428:25  error    Unexpected any. Specify a different type                                                                       @typescript-eslint/no-explicit-any
225:  ##[error]  487:18  error    'error' is defined but never used                                                                              @typescript-eslint/no-unused-vars
226:  ##[error]  730:77  error    Unexpected any. Specify a different type                                                                       @typescript-eslint/no-explicit-any
227:  ##[error]  761:47  error    'icon' is defined but never used                                                                               @typescript-eslint/no-unused-vars
228:  ##[error]  812:21  error    Unexpected any. Specify a different type                                                                       @typescript-eslint/no-explicit-any
229:  ✖ 16 problems (15 errors, 1 warning)
230:  ❌ ESLint found issues in changed files
231:  ##[error]Process completed with exit code 1.
232:  Post job cleanup.

@netlify
Copy link

netlify bot commented Nov 30, 2025

Deploy Preview for incandescent-sfogliatella-f7a090 ready!

Name Link
🔨 Latest commit 6d4f33f
🔍 Latest deploy log https://app.netlify.com/projects/incandescent-sfogliatella-f7a090/deploys/692c5aec54cc6100086df766
😎 Deploy Preview https://deploy-preview-120--incandescent-sfogliatella-f7a090.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 project configuration.

@qodo-code-review
Copy link
Contributor

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Add error handling for localStorage

Wrap localStorage access in try...catch blocks to handle potential errors in
environments where it might be disabled, preventing application crashes.

apps/web/src/hooks/useDeprecationNotice.ts [8-18]

 useEffect(() => {
-  const dismissed = localStorage.getItem(DEPRECATION_NOTICE_KEY) === "true";
-  if (!dismissed) {
+  try {
+    const dismissed = localStorage.getItem(DEPRECATION_NOTICE_KEY) === "true";
+    if (!dismissed) {
+      setIsOpen(true);
+    }
+  } catch (error) {
+    console.error("Could not access localStorage:", error);
+    // Fallback to showing the modal if localStorage is not accessible
     setIsOpen(true);
   }
 }, []);
 
 const onClose = () => {
-  localStorage.setItem(DEPRECATION_NOTICE_KEY, "true");
+  try {
+    localStorage.setItem(DEPRECATION_NOTICE_KEY, "true");
+  } catch (error) {
+    console.error("Could not access localStorage:", error);
+  }
   setIsOpen(false);
 };
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies that accessing localStorage can throw an exception, and proposes adding try...catch blocks to prevent a potential application crash, which significantly improves the hook's robustness.

Medium
  • More

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant