Unable to fetch and Display HTML Content from Public Directory

Need help with Vite + React, where one is unable to fetch and display content from the public directory. Despite following various troubleshooting steps, the content from the projects home.html is not being displayed in the React component. There are no logs in the console of the browser. Following is a snapshot of the network and console logs fro the browser. One understands it isn’t a good practice to post images here but without it one cannot view the actual result or understand the problem.

enter image description here

Project Setup:

├── @vitejs/[email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
└── [email protected]

vite.config.js: In the main project directory

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  build: {
    outDir: 'dist',
    rollupOptions: {
      input: {
        main: 'index.html',
        admin: 'admin.html',
      },
    },
  },
  server: {
    open: true,
    host: '127.0.0.1', // Set host to 127.0.0.1
    historyApiFallback: true, // Ensure SPA fallback
  },
  base: './', // Set base path
});

publicHome.jsx: In the public/src directory

import React, { useEffect, useState } from 'react';

const PublicHome = () => {
  const [content, setContent] = useState('');

  useEffect(() => {
    // Fetch content from home.html in the public directory
    fetch('/home.html')
      .then(response => response.text())
      .then(data => setContent(data))
      .catch(error => console.error('Error fetching home.html content:', error));
  }, []);

  return (
    <div dangerouslySetInnerHTML={{ __html: content }} />
  );
};

export default PublicHome;

index.html: In the main project directory and not in the public directory

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Entry Point</title>
</head>
<body>
    <script>
        // Redirect to home.html as the default landing page
        if (window.location.pathname === '/' || window.location.pathname === '/index.html') {
            window.location.href = 'home.html';
        }
    </script>
</body>
</html>

Steps taken is as follows..

Verified that home.html and test.html are in the public directory.
Ensured the fetch path in publicHome.jsx is correct.
Checked the browser’s developer console for errors.
Cleared NPM cache and rebuilt the project using the following commands multiple times

npm cache clean --force
rmdir /s /q node_modules
rmdir /s /q dist
npm install
npm run build
npx serve -s dist

Despite following the said steps, the content from home.html is not being displayed in the React component. The network request for the HTML files is successful, but the content is not rendered.

Any insights or suggestions on how to resolve this issue would be greatly appreciated. Thank you!

How to Conditionally Render Mobile and Desktop Ads in Django to Prevent JavaScript Overlap?

I’m working on a Django project where I need to display different ads for mobile and desktop users. Currently, both mobile and desktop ad JavaScript is being loaded, which causes conflicts as the mobile ad scripts interfere with the desktop ads.
Here’s a simplified version of my template code:

<div class="ads-mobile">
    {% ad_simple_tag "mobile_ads" %} 
</div>

<div class="ads-desktop">
    {% ad_simple_tag "desktop_ads" %} 
</div>

I’m using Cloudflare, which caches the pages, so I can’t rely on server-side logic (like middleware) to determine which ads to display, as the result would be cached.

I attempted to use JavaScript and/or css to detect the device type and hide/show or remove the ad blocks accordingly, but both ad scripts are still being loaded, causing issues.

How can I ensure that only the relevant ad scripts are loaded based on the user’s device type, considering that the page is cached by Cloudflare? Is there a client-side approach or best practice for handling this kind of situation in Django?

Altering image height / text block height balance depending on screen size

I am trying to work out how to display image heights differently, depending on the screen size the website is viewed on. There are blocks of image and text that sit side by side and are set to appear at a common height at desktop. However at mobile size when elements are stacked I no longer need the common height command to apply. I just need to make sure all the text displays and that the image displays full width, any height. (I no longer need the heights of adjacent blocks to balance at mobile).

I think the common-height command is a js command…

maxHeight: function(){
            if( $commonHeightEl.length > 0 ) {
                if( $commonHeightEl.hasClass('customjs') ) { return true; }
                $commonHeightEl.each( function(){
                    var element = $(this);
                    if( element.find('.common-height').length > 0 ) {
                        SEMICOLON.initialize.commonHeight( element.find('.common-height:not(.customjs)') );
                    }

                    SEMICOLON.initialize.commonHeight( element );
                });
            }
        },
var $counterEl = $('.counter:not(.counter-instant)');
            if( $counterEl.length > 0 ){
                $counterEl.each(function(){
                    var element = $(this);
                    var counterElementComma = $(this).find('span').attr('data-comma');
                    if( !counterElementComma ) { counterElementComma = false; } else { counterElementComma = true; }
                    if( $body.hasClass('device-xl') || $body.hasClass('device-lg') ){
                        element.appear( function(){
                            SEMICOLON.widget.runCounter( element, counterElementComma );
                            if( element.parents('.common-height') ) {
                                SEMICOLON.initialize.maxHeight();
                            }
                        },{accX: 0, accY: -120},'easeInCubic');
                    } else {
                        SEMICOLON.widget.runCounter( element, counterElementComma );
                    }
                });
            }
        },
$commonHeightEl = $('.common-height'),
<div class="row our-services common-height no-gutters">

                <div class="col-md-6 dark ohidden order-2 order-md-1">
                    <div class="h-100" style="background: url(images/our-services-01.jpg) center center / cover no-repeat;">
                    </div>
                </div>

                <div class="col-md-6 dark ohidden order-1 order-md-2">
                    <div class="pb-md-5 h-100" style="background-color: #ce5b28;">
                        <h3 class="uppercase" style="font-weight: 400;">RESIDENTIAL PROJECTS</h3>
                     <p>Families grow and lifestyles change and with it the demand for more space for playrooms, larger eat-in kitchens and bedrooms with en-suites and walk-in wardrobes. We offer a high quality construction service in loft conversions and extensions and have formed strong alliances with architects, surveyors, structural engineers and trade specialists.<br><br>
                        <span class="font-italic">“Heyland &amp; Spratt have steered us through a complex refurbishment and extension project on our house in Camberley. From day one dealing with council bureaucracy to moving in. Throughout he kept us informed and at peace! We are delighted with our new home.”</span>
                        <br>Surita and Gavin, Camberley</p>
                        <h3 class="uppercase" style="font-weight: 400;">BUY TO LET LANDLORDS</h3>
                     <p>Heyland &amp; Spratt Ltd works closely with various London Lettings Agents and private landlords to ensure their property management and maintenance requirements are met quickly, efficiently and on budget.<br><br>
                        <span class="font-italic">“I have always found Heyland &amp; Spratt to be helpful and reliable - their work is completed to a high standard for both large scale projects and smaller general maintenance jobs.”</span><br>Alex, West London Lettings Manager<br><br>
                        <a href="pdfs/HS-Planned-Maintenance-Services.pdf" target="_blank" class="button-white button-rounded">Download our Brochure</a><br><br>
                      <strong>WORKING WITH:</strong><br>
                    <img src="images/savills-white.png" width="95" height="70" alt="Savills"><img src="images/hamptons-white.png" width="152" height="70" alt="Hamptons"><img src="images/knight-frank-white.png" width="154" height="70" alt="Knight Frank"><img src="images/marsh-white.png" width="106" height="70" alt="Marsh"></p>
                    </div>
                </div>

                <div class="col-md-6 dark ohidden ohidden order-3">
                    <div class="pb-md-5" style="background-color: #152c53;">
                        <h3 class="uppercase" style="font-weight: 400;">COMMERCIAL WORK</h3>
                        <br><p>Our commercial and corporate clients demand a straight talking approach, tight time lines and a thorough Health and Safety policy. We specialise in working on building and refurbishing children’s nurseries and schools. We have carried out work in numerous shop fronts, children’s nurseries, Heathrow and City Airports and London’s famous Gherkin Building.<br><br>
Each assignment comes with a dedicated Site Manager from start to finish. Our core team of reliable sub-contractors, specialists and consultants, together with a long-standing trusted workforce, ensures that we always deliver practical, skilled and resourceful solutions.<br><br>
                        <span class="font-italic">“Our company has been using Heyland and Spratt for some years now and within that time they have carried out large refurbishment projects within our nurseries. Every time we have found them to be very professional in their approach to the projects and always with the wellbeing of the children in our nurseries in mind, both during the project and also in the finished work so to enhance the safety of the children and staff within the nursery.”</span><br><br>Nationwide Group of Children’s Nurseries<br><br>
                        <a href="pdfs/HS-Commercial-Education-Projects.pdf" target="_blank" class="button-green-02 button-rounded">Download our Brochure</a><br><br>
                      <strong>WORKING WITH:</strong><br><br>
                      <img src="images/bright-horizons.png" width="152" height="70" alt="Bright Horizons"><img src="images/kedlestone-group.png" width="154" height="70" alt="Kedlestone Group"><img src="images/cadogan.png" width="115" height="70" alt="Cadogan"><img src="images/graphica-display.png" width="97" height="70" alt="Graphica Display"></p>
                     </div>
                </div>

                <div class="col-md-6 dark ohidden order-4">
                    <div class="h-100" style="background: url(images/our-services-02.jpg) center center / cover no-repeat;">
                    </div>
                </div>

                <div class="col-md-6 dark ohidden order-6 order-md-5">
                    <div class="h-100" style="background: url(images/our-services-03.jpg) center center / cover no-repeat;">
                    <a name="commercial-work"></a>
                    
                    </div>
                </div>

                <div class="col-md-6 dark ohidden order-5 order-md-6">
                    <div class="pb-md-5 h-100" style="background-color: #bdc736;">
                    
                        <h3 class="uppercase" style="font-weight: 400; color: #002a53;">BESPOKE JOINERY WORKSHOP</h3>
                        <br><p class="bluetext">Our bespoke joinery workshop specializes in creating custom woodwork solutions for both the commercial and residential sectors. Working directly with the client and cutting out subcontractor means ensures a faster turnaround time, greater efficiency, and significant cost savings for our clients. <br><br>With a focus on craftsmanship and attention to detail, we design and build high-quality, tailored joinery that meets the unique needs of each project. Whether you’re outfitting a commercial space or enhancing a residential property, our skilled team delivers innovative, functional, and beautiful woodwork that elevates any environment.
                     </p> </div>
                </div>
            </div>

I wonder how to add to this command to do something different at mobile size? Or is there a better way to do that which doesn’t involve js, perhaps just CSS. Basically to avoid doing weird image crops at mobile size.

How can I automatically format dates according to the OS’s locale in Angular without manually setting LOCALE_ID?

I am building an Angular application and want to format dates based on the user’s operating system locale (or browser’s default language) without manually setting the LOCALE_ID for each case.

While similar questions suggest setting LOCALE_ID manually (e.g., using navigator.language or HTTP_ACCEPT_LANGUAGE headers), I’m specifically looking for a way to:

Automatically detect the system or browser’s locale.
Display the date and time in the correct format based on that locale.
Avoid manually setting the LOCALE_ID every time I need to use date formatting (e.g., with Angular’s DatePipe or Kendo UI components).
I have already explored solutions where the language/locale is determined by navigator.language, but I’m looking for cleaner or more automatic ways to achieve this.

What I’ve tried:

Using the LOCALE_ID provider to set the locale manually.
Using the HTTP_ACCEPT_LANGUAGE header to determine the language in the backend (e.g., nginx), and then setting the locale for the frontend.
However, I want to avoid specifying a language manually whenever possible. Is there a built-in Angular solution for this? How can I detect and format dates automatically based on the user’s system locale?

import { LOCALE_ID, NgModule } from "@angular/core";
import { LocaleService } from "./shared/service/locale.service";

@NgModule({
  providers: [
    {
      provide: LOCALE_ID,
      deps: [LocaleService],
      useValue: (localeService) => localeService.getLanguage(),
    },
  ],
})
export class AppModule {}

@Injectable({
  providedIn: "root",
})
export class LocaleService {
  getLanguage() {
    return "fr-FR"; // manually setting for demonstration
  }
}

What am I missing here? How can I achieve automatic date formatting based on the user’s operating system or browser settings?

Key Differences:

I’m asking for a solution where date formatting is automatically handled based on the OS or browser locale without needing to manually configure LOCALE_ID each time.
The goal is to detect the locale dynamically, not hard-code it.

How can I read and display the OS-specific date and time format in Angular? [duplicate]

I am working on an Angular application and need to display the date and time based on the user’s operating system locale. How can I retrieve and format the date and time according to the OS-specific format in Angular?

Is there a built-in method or library that allows me to automatically detect and display the date and time in the correct format for different operating systems or locales? Any guidance or examples would be greatly appreciated!

How to handle tables with variable columns in Tiptap?

I created the following table:

<table>
  <tbody>
    <tr>
      <th>Header</th>
      <th>Header</th>
      <th>Header</th>
    </tr>
    <tr>
      <td>one</td>
    </tr>
    <tr>
      <td>one</td>
      <td>two</td>
    </tr>
    <tr>
      <td>one</td>
      <td>two</td>
      <td>three</td>
    </tr>
  </tbody>
</table>

When I use the addRowAfter method, a new row is automatically added, but it also inserts extra elements to match the maximum number of columns in the table. For example, if I add a new row after the first row (one), it automatically adds extra elements, like this:

<tr>
  <td></td>
  <td></td>
  <td>one</td>
</tr>

How can I prevent Tiptap from automatically adding extra elements when a new row is added, and maintain the variable column structure?

The code currently in use is as follows.

'use client'

import { useEditor, EditorContent } from '@tiptap/react'
import Table from '@tiptap/extension-table'
import TableCell from '@tiptap/extension-table-cell'
import TableHeader from '@tiptap/extension-table-header'
import TableRow from '@tiptap/extension-table-row'
import StarterKit from '@tiptap/starter-kit';

const Tiptap = () => {
    const editor = useEditor({
        extensions: [
            StarterKit,
            Table,
            TableCell,
            TableHeader,
            TableRow
        ],
        content: `<table>
        <tbody>
        <tr>
            <th>Header</th>
            <th>Header</th>
            <th>Header</th>
        </tr>
        <tr>
            <td>one</td>
        </tr>
        <tr>
            <td>one</td>
            <td >two</td>
        </tr>
        <tr>
            <td>one</td>
            <td>two</td>
            <td>three</td>
        </tr>
        </tbody>
        </table>
        `,
    })

    const onClickTest = () => {
        editor.commands.addColumnAfter()
    }

    return <>
        <div style={{ margin: '10px' }}>
            <button style={{ background: 'grey', margin: '10px', color: 'white' }} onClick={onClickTest}>addColumnAfter</button>
            <EditorContent style={{ border: '1px solid black', margin: '10px' }} editor={editor} />
        </div>
    </>
}

export default Tiptap

Waiting for all async requests to compelete with contcatMap

assuming that there are multiple ajax requests that need to execute one by one sequentially, the next one should be called when the previous is done. I can achieve that by using the concatMap RxJs function, right? The question is how should I wait for all of the ajax requests passed to the concatMap to complete?

from(elementsList).pipe(concatMap((elem) => this.simpleFunc(elem)))).subscribe();

should be something like: waitForAll

or can I somehow use it in combination with forkJoin ?

How do I get the index of a div close to a button , if the button got clicked?

let add_btn = document.querySelector('.add');
let container = document.querySelector('.container');
let createNote = document.querySelector('.create-note');
let create = document.querySelector('.create-btn');
let close = document.querySelector('.close-btn');
let text = '';
let editNote = document.querySelector('.edit-note');
let textArea = document.querySelector('textarea');
let closeBtn = document.querySelector('.close-btn2');
let editBtn = document.querySelector('.edit-btn');
let textArea2 = document.querySelector('.textarea2');

arr = [];

add_btn.addEventListener('click', () => {
  textArea.value = '';
  createNote.style.display = 'block';
});

close.addEventListener('click', () => {
  createNote.style.display = 'none';
});

create.addEventListener('click', () => {
  let note = document.createElement('div');
  note.className = 'note';
  
  let innerNote = document.createElement('div');
  innerNote.className = 'inner-note';
  
  text = textArea.value;
  innerNote.textContent = text;
  
  let edit = document.createElement('i');
  edit.className = 'fa-solid fa-edit';
  
  let del = document.createElement('i');
  del.className = 'fa-solid fa-trash';
  
  if (!textArea.value) {
    return;
  } else {
    note.appendChild(innerNote);
    note.appendChild(edit);
    note.appendChild(del);
    container.appendChild(note);
    createNote.style.display = 'none';
    note.style.display = 'block';
    
    edit.addEventListener('click', () => {
      let text = edit.closest('.note');
      let text2 = text.querySelector('.inner-note');
      textArea2.value = text2.textContent;
      editNote.style.display = 'block';
    });

    del.addEventListener('click', () => {
      let noteText = document.querySelectorAll('.inner-note');
      let noteText1 = Array.from(noteText);
      let index = noteText1.indexOf(innerDiv1);
      arr.splice(index, 1);
      container.removeChild(note);
      console.log(arr);
    });
    
    noteText = document.querySelectorAll('.inner-note');
    noteText.forEach((val, index) => {
      arr[index] = val.textContent;
      IndexofNote = index;
    });
    
    console.log(`noteText has a length of ${noteText.length} `);
    console.log(
      `Note at index ${IndexofNote} ${arr[IndexofNote]} length of array is ${arr.length} `
    );
  }
});

editBtn.addEventListener('click', () => {
  innerDiv1.textContent = textArea2.value;
  editNote.style.display = 'none';
  noteText.forEach((val, Index) => {
    arr[Index] = val.textContent;
    console.log(`Note at index ${Index} ${arr[Index]}`);
  });
  console.log(arr);
});

container.addEventListener('click', (event) => {
  let trgt = event.target;
  if (!trgt.matches('.fa-edit') && !trgt.matches('.fa-trash')) return;
  const parent = trgt.closest('.note');
  innerDiv1 = parent.querySelector('.inner-note');
  if (trgt.matches('.fa-edit')) {
    editNote.style.display = 'block';
  }
});

closeBtn.addEventListener('click', () => {
  editNote.style.display = 'none';
});
  <div class="container">
    <div class="add">
      <i class = 'fa-solid fa-plus'></i>
    </div>
    <div class="create-note">
      <h1 class = 'heading'>New Note</h1>
      <textarea name="" id="" placeholder="Enter your note..."></textarea>
      <button class = 'create-btn'>Create Note</button>
      <button class = 'close-btn'>Close</button>
    </div>
    <div class="edit-note">
      <h1 class = 'heading'>Edit Note</h1>
      <textarea name="" id="" class = 'textarea2'></textarea>
      <button class = 'edit-btn'>Edit Note</button>
      <button class = 'close-btn2'>Close</button>
    </div>
  </div>

I am able to delete the element that got clicked, how do I remove it from the array, that contains all the textContent of the elements.I have tried using Event Delegation, but still I am trying to understand how do I get the index of the element that got clicked, and pass that index to arr.splice(index,1), can you suggest something , it would be really helpful

Browser extension injected style conflict

I’m developing a browser extension using WXT and Vite. My extension’s UI is injected into web pages via content script, but I’m getting style conflicts where the webpage’s styles override my extension’s styles (causing a black border around certain elements like linkedin)

What I Tried:

  • using all: initial in CSS to reset stuff
  • trying a shadow dom but that messed up all my CSS

Anybody else experienced this before?

black border around element

Can I upload to the Instagram API without app approval?

The docs say “Instagram requires successful completion of the app review process before your app can access live data.” Does that mean I cant use the /userid/media endpoint to upload videos?

I tried to do so using the Graph API Explorer but I kept having oAuth errors but I dont want those fixed right now.

Thank you for your help.

the condition is not working that determines the hit of a computer in a naval battle, are there any options for fixing it?

please tell me how to make the computer able to finish off the enemy in this code, in my code it only hits once and that’s it, and then it misses as programmed, and again hits but does not finish off the ship. What is the problem, perhaps in the condition as I define it, or something else, tell me, I have already written to the jpt chat, but nothing still gives the wrong code.

enter code here

initialize() {
    this.player.board.ships = [];
    this.player.board.grid = Array(10).fill().map(() => Array(10).fill(null));
    this.computer.board.ships = [];
    this.computer.board.grid = Array(10).fill().map(() => Array(10).fill(null));

    this.player.setupShips();
    this.computer.setupShips();
    this.player.setEnemyBoard(this.computer.board);
    this.computer.setEnemyBoard(this.player.board);
    this.gameActive = false;
    this.currentTurn = 'player';
    this.computerLastHit = null;
    this.computerHits = [];
    this.computerDirection = null;
}

playerTurn(row, col) {
    if (!this.gameActive || this.currentTurn !== 'player') return null;
    const target = this.computer.board.getCell(row, col);
    if (target && target.hits > 0 && target.isSunk()) return null;
    const wasAttacked = target && target.hits > 0;
    if (target instanceof Ship && !wasAttacked) {
        target.hit(row, col);
    }
    this.currentTurn = 'computer';
    return { row, col, hit: target instanceof Ship };
}

computerTurn() {
    if (!this.gameActive || this.currentTurn !== 'computer') return null;
    let row, col;

    
    if (this.computerLastHit) {
        const lastRow = this.computerLastHit.row;
        const lastCol = this.computerLastHit.col;
        const directions = [
            [0, 1], [0, -1], [1, 0], [-1, 0] 

        ];
        if (this.computerHits.length >= 2 && !this.computerDirection) {
            const [firstRow, firstCol] = this.computerHits[0];
            const [secondRow, secondCol] = this.computerHits[1];
            if (firstRow === secondRow) {
                this.computerDirection = 'horizontal';
            } else if (firstCol === secondCol) {
                this.computerDirection = 'vertical';
            }
        }

        
        if (this.computerDirection) {
            const directionOptions = this.computerDirection === 'horizontal'
                ? [[0, 1], [0, -1]] 
                : [[1, 0], [-1, 0]]; 
            for (const [dr, dc] of directionOptions) {
                row = lastRow + dr;
                col = lastCol + dc;
                if (row >= 0 && row < 10 && col >= 0 && col < 10) {
                    const target = this.player.board.getCell(row, col);
                    if (target && target.hits === 0) {
                        target.hit(row, col);
                        if (target instanceof Ship) {
                            this.computerLastHit = { row, col };
                            this.computerHits.push([row, col]);
                            if (target.isSunk()) {
                                this.computerLastHit = null;
                                this.computerHits = [];
                                this.computerDirection = null;
                            }
                        } else {
                            this.computerDirection = null; 
                        }
                        this.currentTurn = 'player';
                        return { row, col, hit: target instanceof Ship };
                    }
                }
            }
            this.computerLastHit = null;
            this.computerHits = [];
            this.computerDirection = null;
        } else {
            
            for (const [dr, dc] of directions) {
                row = lastRow + dr;
                col = lastCol + dc;
                if (row >= 0 && row < 10 && col >= 0 && col < 10) {
                    const target = this.player.board.getCell(row, col);
                    if (target && target.hits === 0) {
                        target.hit(row, col);
                        if (target instanceof Ship) {
                            this.computerLastHit = { row, col };
                            this.computerHits.push([row, col]);
                            if (target.isSunk()) {
                                this.computerLastHit = null;
                                this.computerHits = [];
                            }
                        }
                        this.currentTurn = 'player';
                        return { row, col, hit: target instanceof Ship };
                    }
                }
            }
            this.computerLastHit = null;
            this.computerHits = [];
        }
    }


    let attempts = 0;
    const maxAttempts = 100;
    do {
        row = Math.floor(Math.random() * 10);
        col = Math.floor(Math.random() * 10);
        const target = this.player.board.getCell(row, col);
        if (!target || target.hits === 0) break;
        attempts++;
        if (attempts >= maxAttempts) return null;
    } while (true);

    const target = this.player.board.getCell(row, col);
    if (target instanceof Ship) {
        target.hit(row, col);
        this.computerLastHit = { row, col };
        this.computerHits = [[row, col]];
    }
    this.currentTurn = 'player';
    return { row, col, hit: target instanceof Ship };
}

startGame() {
    this.gameActive = true;
    this.currentTurn = 'player';
}

checkWinner() {
    const computerSunk = this.computer.board.ships.every(ship => ship.isSunk());
    if (computerSunk) return 'player';

    const playerSunk = this.player.board.ships.every(ship => ship.isSunk());
    if (playerSunk) return 'computer';

    return null;
}

endGame(winner) {
    this.gameActive = false;
    alert(winner === 'player' ? 'You win!' : 'Computer wins!');
}

restartGame() {
    this.initialize();
    this.startGame();
}

}

Error handling in NodeJS service classes and controllers

I am very new to both JavaScript and NodeJS and am struggling to grapple some of the concepts surrounding async, promises, and others, and especially how to structure the code properly. I am trying to encapsulate some of the functionality in one of my route controllers by extracting it into a Service class. The initial code I have is something like the below example:

import Person from "../models/person.js";
import mongoose from "mongoose";

async function getPerson(req, res) {
    try{
        if(!mongoose.Types.ObjectId.isValid(req.params.id)){
            return res.status(400).send('Invalid ID');
        }

        // Query DB
        const person = await Person.findById(req.params.id);
        if (!person) {
            res.status(404).send('Not found');
        }

        // Verify
        if (person.Age < 18) {
            res.status(403).send('Cannot query for minors');
        }
        
        // Success
        res.status(200).send(person)

    } catch (error) {
        console.error(error);
        res.status(500).send("Error retrieving media");
    }
}

export { getPerson };

This is the controller for a route like “www.projectdomain.com/person/99” to retrieve the Person data from the person with ID 99. There are a few different failure modes with error codes 400, 404, 403 or 500 depending on the circumstances.

Now, I want to extract some of this into a more general Service class that isn’t necessarily tied to a HTTP get request, and without access to the req and res objects. I have the following:

import Person from "../models/person.js";
import mongoose from "mongoose";

class PersonService {
    getPerson(id) {
        if(!mongoose.Types.ObjectId.isValid(id)){
            throw new Error('Invalid ID');
        }

        const person = await Person.findById(id);
        if (!person) {
            throw new Error("Not found");
        }

        // Verify
        if (person.Age < 18) {
            throw new Error('Cannot query for minors');
        }
        
        // Success
        return person;
    }
}

export default PersonService;

I don’t know of a better way to throw informed errors that can be translated to a HTTP error code in the outside context. The Controller would now look something like:

import PersonService from "../services/PersonService.js";

async function getPerson(req, res) {
    const personService = new PersonService();
    try{
        const personData = await personService.getPerson(req.params.id);
        res.status(200).send(personData)

    } catch (error) {
        console.error(error);
        res.status(500).send("Error retrieving media");
    }
}

export { getPerson };

The motivation is of course that we might want to reuse the logic for retrieving a person’s data from the database in a context where we don’t have res and req. However, any errors thrown by the Service class will now get caught by the outside Controller and returned as a status 500. How can I implement Services with proper error handling in NodeJS? How can I abstract some functionality to Service classes without losing the ability to return informed error codes?

Is there a way to save a dash app to an offline html file, while preserving all interactions (hover, checkbox filtering etc)?

I’ve tried javascript code, but I’m not familiar with JS and the filtering is not working properly. fig.write_html doesn’t work because the checkboxes will not show up.

For context, I have 2 main traces on my graph, go.Scatter and px.timeline. The checkbox will filter which medications show up on the y-axis, and remove/add the scatter points and horizontal bars accordingly.

server = Flask(__name__)
app = Dash(__name__, server = server)

ipharm_meds = list(ipharm['meds'].unique())
eimr_meds = list(eimr['meds'].unique())
unique_meds = list(set(ipharm_meds + eimr_meds))

'''
wrapping text so that it can appear nicely on graph on hover
need to play with width value according to the length of text
'''

wrapper = textwrap.TextWrapper(width = 100)
notes['summary'] = notes['summary'].apply(lambda x: wrapper.fill(x))
notes['summary'] = notes['summary'].apply(lambda x: x.replace('n', '<br>'))

app.layout = html.Div([
    html.Div([
        dcc.Checklist(
            id = 'med-checklist',
            options = [{'label': med, 'value': med} for med in unique_meds],
            value = unique_meds,
            inline = True
        ),
    ], style = {'width': '100%', 'padding': '10px'}),
    dcc.Graph(id = 'timeline-graph')
])

@app.callback(
    Output('timeline-graph', 'figure'),
    Input('med-checklist', 'value')
)

def update_graph(selected_meds):

    # filtered based on checkbox values
    ipharm_filtered = ipharm[ipharm['meds'].isin(selected_meds)]
    ipharm_filtered['Start'] = pd.to_datetime(ipharm_filtered['Start'])
    ipharm_filtered['End'] = pd.to_datetime(ipharm_filtered['End'])
    ipharm_filtered['Start_str'] = ipharm_filtered['Start'].dt.strftime("%d-%m-%Y")
    ipharm_filtered['End_str'] = ipharm_filtered['End'].dt.strftime("%d-%m-%Y")

    # broken barh for ipharm
    fig = px.timeline(
        ipharm_filtered,
        x_start = 'Start',
        x_end = 'End',
        y = 'meds',
        hover_data = {'Start': False,
        'End': False,
        'Start_str': True,
        'End_str': True,
        'FREQUENCY': True,
        'DOSAGE INSTRUCTION': True,
        'dose': True}
    )

    # Add scatter for eimr
    for med in selected_meds:
        med_data = eimr[eimr['meds'] == med]
        if not med_data.empty:
            fig.add_trace(go.Scatter(
                x=med_data['ServingDateTime'],
                y=[med] * len(med_data),
                mode='markers',
                marker=dict(size=5, color = 'red'),
                name=f'eIMR_{med}',
                text=med_data['Dose'],
                hoverinfo='text',
                showlegend=False
                )
            )

    # Add shaded regions for ward duration
    for _, row in ward.iterrows():
        fig.add_vrect(
            x0=row['Adm.Date'],
            x1=row['Disch.Date'],
            fillcolor='grey',
            opacity=0.5,
            line_width=0
            )

    # Add vertical lines for consults
    for _, row in notes.iterrows():
        fig.add_shape(
            type = 'line',
            x0 = row['Date'],
            x1 = row['Date'],
            y0 = 0,
            y1 = 1,
            yref = 'paper',
            line = dict(color = 'black', width = 1)
        )

        # Add scatter points vertically on every vline
        # for hover functionality
        for med in selected_meds:
            fig.add_trace(go.Scatter(
                x = [row['Date']],
                y = [med],
                opacity = 0, # make points invisible
                mode = 'markers',
                marker = dict(size = 5, color = 'black'),
                text = row['summary'],
                hoverinfo = 'text',
                showlegend=False
            ))

    # Update layout
    fig.update_layout(
        xaxis=dict(showgrid=True),
        yaxis=dict(showgrid=True)
    )

    return fig

def save_html():
    # Create the initial figure with all medications

    initial_fig = update_graph(unique_meds)

    # Create JavaScript function for checkbox interactivity
    checkbox_js = """
    <script>
    function updateVisibility() {
        var checkboxes = document.getElementsByClassName('med-checkbox');
        var gd = document.getElementById('timeline-graph');
        var traces = gd.data;
        var selectedMeds = Array.from(checkboxes)
            .filter(cb => cb.checked)
            .map(cb => cb.value);
        var visibility = traces.map(trace => {
            if (trace.y && trace.y.length > 0) {
                return selectedMeds.includes(trace.y[0]) ||
                        selectedMeds.includes(trace.name?.replace('eIMR_', ''));
            }
            return false;
        });
        Plotly.update('timeline-graph',
            {visible: visibility},
            {height: selectedMeds.length * 40},
                
        );
    }

    window.addEventListener('load', function() {
        var gd = document.getElementById('timeline-graph');
        window.originalData = JSON.parse(JSON.stringify(gd.data));
    });
    </script>
    """

   Create HTML string with checkboxes
    checkbox_html = """
    <div style="width:100%; padding:10px">
    """

    for med in unique_meds:
        checkbox_html += f"""
    <label style="margin-right:10px">
    <input type="checkbox" class="med-checkbox" value="{med}"
                    checked onclick="updateVisibility()">
            {med}
    </label>
        """

    checkbox_html += "</div>"

   # Convert figure to HTML
    fig_html = initial_fig.to_html(
        full_html=False,
        include_plotlyjs=True,
        div_id='timeline-graph'
    )

   # Combine everything
    full_html = f"""
    <html>
    <head>
    <title>Medication Timeline</title>
        {checkbox_js}
    </head>
    <body>
        {checkbox_html}
        {fig_html}
    </body>
    </html>
    """
    with open('graph_v1.html', 'w', encoding = 'utf-8') as f:
        f.write(full_html)

if __name__ == "__main__":
    save_html()

How can I prevent my node production deployment from installed devDependencies?

I’m running into an issue with the production deployment of my Node app. When I deploy to dev it’s fine. When I deploy to production, the build errors on the node-sass package. Based on this SO thread what may be happening here is Node is not trying to install that package but IS trying to resolve it.

adding the –production flag did not prevent the issue.

I don’t need that package in production so I’m not really interested in correcting whatever that specific error is, but I’m trying to find a way to prevent Node from even resolving any devDependencies in the first place.

I’ve thought of possibly creating a feature branch in git that I develop on, removing the dev dependencies for the release branch (which is the production branch), and adding package.json to .gitignore

But these seems like a hassle. Is there a better way?