Can’t connect to my mongdb database using mongoose (nodejs)

Im trying to connect to my database. Its working if i use mongodb compass but then im using mongoose to connect it doesn’t work.

I keep getting this error:

Mongoose connection error MongoServerSelectionError: Client network socket disconnected before secure TLS connection was established
    at Topology.selectServer (C:UsersgerarDesktopHtmlPractica-HolaMundohtmlPracticaAPIrestnode_modulesmongodblibsdamtopology.js:320:38)
    at async Topology._connect (C:UsersgerarDesktopHtmlPractica-HolaMundohtmlPracticaAPIrestnode_modulesmongodblibsdamtopology.js:204:28)
    at async Topology.connect (C:UsersgerarDesktopHtmlPractica-HolaMundohtmlPracticaAPIrestnode_modulesmongodblibsdamtopology.js:156:13)
    at async topologyConnect (C:UsersgerarDesktopHtmlPractica-HolaMundohtmlPracticaAPIrestnode_modulesmongodblibmongo_client.js:233:17)
    at async MongoClient._connect (C:UsersgerarDesktopHtmlPractica-HolaMundohtmlPracticaAPIrestnode_modulesmongodblibmongo_client.js:246:13)
    at async MongoClient.connect (C:UsersgerarDesktopHtmlPractica-HolaMundohtmlPracticaAPIrestnode_modulesmongodblibmongo_client.js:171:13)
    at async NativeConnection.createClient (C:UsersgerarDesktopHtmlPractica-HolaMundohtmlPracticaAPIrestnode_modulesmongooselibdriversnode-mongodb-nativecon
nection.js:320:3

This is my api.js file

const express = require("express");
const mongoose = require("mongoose");
const user = require("./user.controller");
const port = 3000;
const app = express();

app.use(express.json());

mongoose.connect(
  "mongodb+srv://geraboy:(password)@holamundocert.wfq1b.mongodb.net/miApp?retryWrites=true&w=majority&appName=HolaMundoCert"
);

mongoose.connection.on("connected", () => {
  console.log("Mongoose connected to MongoDB");

  app.listen(port, () =>
    console.log(`Starting server on port ${port}`)
  );
});

mongoose.connection.on("error", (error) => {
  console.log("Mongoose connection error", error);
});

app.get("/", user.list);
app.post("/", user.create);
app.get("/:id", user.get);
app.put("/:id", user.update);
app.patch("/:id", user.update);
app.delete("/:id", user.destroy);

This is my package.json

{
  "name": "apirest",
  "version": "1.0.0",
  "description": "",
  "main": "api.js",
  "scripts": {
    "start": "nodemon api.js"
  },
  "keywords": [],
  "author": "Gerardo Acedo",
  "license": "ISC",
  "dependencies": {
    "express": "^4.21.2",
    "mongodb": "^6.12.0",
    "mongoose": "^8.9.5"
  },
  "devDependencies": {
    "nodemon": "^3.1.9"
  }
}

I tried setting the IP network access to 0.0.0.0/0 on the mongodb cluster’s website, also tried adding my own IP direction.
Already created inbound and outbound permissions for port 27017.

Also tried setting my DNS to 8.8.8.8

Google AppScript troubleshooting using 3rd party api (api2pdf) for pdf transformation

I’ve copied the code below that I’m trying to work with to transform emails with specific subject into another format. The trick is I need to accept multiple types of attachments but transform them into pdf & make it so that the documents get split up into multiple documents if larger then 5 pages (as documents are being sent out as faxes). I keep running into 405 errors with api2pdf calls but can’t seem to figure out what I am missing. Any assistance would be greatly appreciated !!

Code:

// Global constants
const ERRORNOTIFICATIONEMAIL = '[email protected]';
const FIXEDDESTINATIONEMAIL = '[email protected]';
const EMAIL_DOMAIN = 'Domain';
const MY_LABEL = 'Processed';
const ERROR_LABEL = 'Error Processing';
const ENABLE_PDF_SPLITTING = true;
const API2PDF_KEY = ''; // Your confirmed valid Api2Pdf API key
const PAGE_LIMIT = 5;

/**
 * Main function to forward emails with processed attachments.
 */
function forwardMail() {
  const threadsWithAttachments = [];
  const oneWeekAgo = new Date();
  oneWeekAgo.setDate(oneWeekAgo.getDate() - 7);
  const formattedDate = Utilities.formatDate(oneWeekAgo, Session.getScriptTimeZone(), 'yyyy/MM/dd');

  try {
    const searchQuery = `subject:"@${EMAIL_DOMAIN}" -label:"Processed" -label:"Error Processing" after:${formattedDate}`;
    Logger.log(`Search query: ${searchQuery}`);
    const threads = GmailApp.search(searchQuery);
    Logger.log(`Found ${threads.length} threads matching query.`);

    for (const thread of threads) {
      const messages = thread.getMessages();
      let hasError = false;

      for (const message of messages) {
        const subject = message.getSubject();
        const senderEmail = message.getFrom();
        const threadId = thread.getId();
        const labels = thread.getLabels();
        const isProcessed = labels.some(label => label.getName() === MY_LABEL || label.getName() === ERROR_LABEL);

        Logger.log(`Checking thread: Subject="${subject}", Sender="${senderEmail}", ThreadID="${threadId}", Processed=${isProcessed}`);

        if (!isProcessed) {
          Logger.log(`Processing thread: Subject="${subject}", Sender="${senderEmail}", ThreadID="${threadId}`);
          try {
            const attachments = message.getAttachments();
            Logger.log(`Attachments found: ${attachments.length}`);
            attachments.forEach(att => Logger.log(`Attachment: ${att.getName()}, Type: ${att.getContentType()}`));

            const splitAttachments = attachments
              .map(attachment => transformAndSplitAttachment(attachment, thread))
              .flat()
              .filter(att => att !== null);

            Logger.log(`Processed split attachments: ${splitAttachments.length}`);
            if (splitAttachments.length > 0) {
              splitAttachments.forEach(splitAttachment => {
                const newSubject = transformSubject(subject);
                Logger.log(`Sending email with attachment: ${splitAttachment.getName()}`);
                sendEmailWithAttachments(newSubject, [splitAttachment], subject, senderEmail, threadId);
              });
              threadsWithAttachments.push(thread);
            } else {
              Logger.log('No valid split attachments to forward.');
            }
          } catch (error) {
            Logger.log(`Error processing thread: ${error.message}`);
            sendErrorNotification('Error processing thread', error.message);
            hasError = true;
          }
        }
      }

      if (hasError) {
        addErrorLabel(thread);
      }
    }

    if (threadsWithAttachments.length > 0) {
      addProcessedLabel(threadsWithAttachments);
      Logger.log(`Processed label added to ${threadsWithAttachments.length} threads.`);
    } else {
      Logger.log('No threads found with attachments.');
    }
  } catch (error) {
    Logger.log(`Error in forwardMail: ${error.message}`);
    sendErrorNotification('Error in forwardMail', error.message);
  }
}

/**
 * Transforms and splits attachment if necessary.
 */
function transformAndSplitAttachment(attachment, thread) {
  try {
    const originalBlob = attachment.copyBlob();
    const sanitizedFileName = attachment.getName().replace(/[^a-zA-Z0-9]/g, '_');

    if (originalBlob.getContentType() === MimeType.PDF) {
      Logger.log(`Processing PDF directly: ${sanitizedFileName}`);
      const pageCount = getPageCountUsingGoogleDrive(originalBlob);

      if (ENABLE_PDF_SPLITTING && pageCount > PAGE_LIMIT) {
        const splitPdfs = splitPdfUsingApi2Pdf(originalBlob, sanitizedFileName, pageCount);
        Logger.log(`Split PDFs created: ${splitPdfs.length}`);
        return splitPdfs;
      }

      Logger.log(`PDF does not require splitting. Pages: ${pageCount}`);
      return [originalBlob];
    } else {
      throw new Error('Attachment is not a valid PDF.');
    }
  } catch (error) {
    Logger.log(`Error transforming and splitting attachment: ${error.message}`);
    sendErrorNotification('Error transforming attachment', error.message);
    addErrorLabel(thread);
    return null;
  }
}

/**
 * Determines the number of pages in a PDF using Google Drive.
 */
function getPageCountUsingGoogleDrive(pdfBlob) {
  try {
    const tempFile = DriveApp.createFile(pdfBlob);
    const pdfContent = tempFile.getBlob().getDataAsString();
    const pages = (pdfContent.match(//Contents/g) || []).length;

    Logger.log(`Detected ${pages} pages in the PDF.`);
    tempFile.setTrashed(true); // Cleanup
    return pages;
  } catch (error) {
    Logger.log(`Error getting page count: ${error.message}`);
    throw new Error('Failed to determine page count.');
  }
}

/**
 * Splits a PDF into smaller parts using Api2Pdf.
 */
function splitPdfUsingApi2Pdf(pdfBlob, baseFileName, pageCount) {
  const blobs = [];
  try {
    // Upload PDF to Google Drive to get a shareable URL
    const pdfFile = DriveApp.createFile(pdfBlob);
    const pdfUrl = pdfFile.getUrl(); // This should return a proper URL

    for (let startPage = 0; startPage < pageCount; startPage += PAGE_LIMIT) {
      const endPage = Math.min(startPage + PAGE_LIMIT - 1, pageCount - 1); // Updated to handle zero-based index

      const payload = {
        url: pdfUrl, // Use the shareable URL
        start: startPage,
        end: endPage,
        inline: true, // Ensure the PDF is processed inline
        fileName: `${baseFileName}_Pages_${startPage + 1}_to_${endPage + 1}.pdf`, // Friendly file name
      };

      const options = {
        method: 'POST',
        contentType: 'application/json',
        muteHttpExceptions: true,
        payload: JSON.stringify(payload),
      };

      // Endpoint with API key as a query parameter
      const endpoint = `https://v2.api2pdf.com/pdfsharp/extract-pages?apikey=${API2PDF_KEY}`;
      Logger.log(`Sending request to: ${endpoint}`);
      const response = UrlFetchApp.fetch(endpoint, options);
      const jsonResponse = JSON.parse(response.getContentText() || '{}');

      Logger.log(`Api2Pdf Response Code: ${response.getResponseCode()}`);
      Logger.log(`Api2Pdf Response Body: ${JSON.stringify(jsonResponse, null, 2)}`);

      if (jsonResponse.Success && jsonResponse.FileUrl) {
        const blob = UrlFetchApp.fetch(jsonResponse.FileUrl).getBlob();
        blob.setName(`${baseFileName}_Pages_${startPage + 1}_to_${endPage + 1}.pdf`);
        blobs.push(blob);
      } else {
        throw new Error(`Api2Pdf error: ${jsonResponse.Error || 'Unknown error'}`);
      }
    }
  } catch (error) {
    Logger.log(`Error splitting PDF using Api2Pdf: ${error.message}`);
    sendErrorNotification('Error splitting PDF using Api2Pdf', error.message);
    return [];
  }
  return blobs;
}

/**
 * Sends error notifications.
 */
function sendErrorNotification(subject, body) {
  GmailApp.sendEmail(ERRORNOTIFICATIONEMAIL, `ERROR: ${subject}`, body);
}

/**
 * Adds a "Processed" label to threads.
 */
function addProcessedLabel(threads) {
  const label = GmailApp.createLabel(MY_LABEL);
  threads.forEach(thread => thread.addLabel(label));
}

/**
 * Adds an "Error Processing" label to threads.
 */
function addErrorLabel(thread) {
  const label = GmailApp.createLabel(ERROR_LABEL);
  thread.addLabel(label);
}

/**
 * Sends an email with attachments.
 */
function sendEmailWithAttachments(subject, attachments, originalSubject, senderEmail, threadId) {
  GmailApp.sendEmail(FIXEDDESTINATIONEMAIL, subject, `Processed email from ${senderEmail} (Thread ID: ${threadId})nnOriginal Subject: ${originalSubject}`, {
    attachments: attachments,
  });
}

Trying to use api2pdf api to split pdf attachments into multiple files. Tried a lot of variations but not really sure what I am missing. Latest error code is 422

What does the empty function body return in JS?

Does an empty function body implicitly return undefined in JS?

console.log((() => {})())

Clearly it does right? The console outputs undefined in the example above…

// but let's do something crazy

console.log(((undefined) => undefined)("defined"));

console.log((() => {
  var undefined = "defined";
  return undefined; 
})());

Ok so we can redeclare undefined locally (or globally without strict mode)

console.log(((undefined) => {
  var undefined = "defined_again";
})("defined"));

But the output is still undefined, so it seems it’s not implicitly returning undefined, but instead treating the entire expression as void(expr).

This also seems to be the case in TypeScript:

const result = (() => {})()
//    ^?: const result: void

So if the empty function body does implicitly return undefined as outlined in the spec, then why does JS ignore lexical scope for undefined if it’s redeclared in the function body?

Obviously at the end of the day the result is undefined, but I’m more curious about the actual implementation behavior of what is implicitly returned?

Does the empty function body actually return something?

Quelle sont les étapes à suivre pour créer un site web d’un collège et d’une application mobile [closed]

Je voudrais développer un site web pour un collège et une application mobile j’aurai besoin d’aide

J’ai essayé de codé mais j’attends de vous une aide plus approfondie et un code pour commencer donc j’aimerais quelques choses de bien pour pouvoir mieux réaliser ce projet en toute confiance avec une aide se serai bien

Value from a spread object does not correctly assign the type when the type is a union

This is kind of a strange edge case that it took me several hours to figure out, but I don’t know what typescript rule is causing it, so I’m curious if this is something I’m not aware of, or if it’s a bug in my typescript interpreter.

I have a type where I want an extra field (allowedValues) only if a different field has a particular value (selection). I wrote a union type that looks like this:

type AttributeTypeOptional = {
  type: 'textField';
} | {
  type: 'selection';
  allowedValues: {[key: string]: string};
};

Since I had a lot of mock data, I didn’t want to write the type over and over, so I made a default object and then spread it into each result. (There would also be other fields here, but they aren’t relevant to the problem.)

const defaults = {
  type: 'textField';
};

When I did this, objects with the default value for type: textField would expect allowedValues. BUT, if I included the type directly in the object, the error would go away.

results: [
  {
    ...defaults, // <-- expects allowedValues (incorrectly).
  },
  {
    ...defaults,
    type: 'textField', // <-- does not expect allowedValues (correct)
  },
  {
    ...defaults,
    type: 'selection', // <-- expects allowedValues (correct)
    allowedValues: {"1": "1", "2": "2"}
  },
]

It seems like including a value from a spread shouldn’t have an effect on whether the type gets correctly asserted. I’ve also tried this with enums instead of strings and had the same problem. (I actually rewrote this when I deduced that the enum wasn’t part of the problem as I originally thought.)

The specific error I’m getting is:

Type ‘{ name: string; description: string; type: string; }’ is not assignable to type ‘AttributeType’.
Type ‘{ name: string; description: string; type: string; }’ is not assignable to type ‘{ type: “selection”; allowedValues: { [key: string]: string; }; } & { name: string; description: string | null; }’.
Property ‘allowedValues’ is missing in type ‘{ name: string; description: string; type: string; }’ but required in type ‘{ type: “selection”; allowedValues: { [key: string]: string; }; }’.

Here’s a link to a slightly more complete version with the error in TS 5.7.3:

StripeJS collectBankAccount currently only accepts the USBankAccount payment method type

I’m trying to use Stripe to collect and save a user’s bank account information to give them a payout at a later date. Even though I’m explicitly only supporting US bank accounts, I keep getting the following error on my frontend:

{
    "code": "Failed",
    "declineCode": null,
    "localizedMessage": "collectBankAccount currently only accepts the USBankAccount payment method type.",
    "message": "collectBankAccount currently only accepts the USBankAccount payment method type.",
    "stripeErrorCode": null,
    "type": null
}

Here is a snippet of my frontend React Native JavaScript code:

const { error } = await collectBankAccountForSetup({
    clientSecret: PaymentSetupIntent, // payment setup intent secret from the backend
    params: {
        payment_method_type: 'us_bank_account',
        payment_method_data: {
            billing_details: {
                name: `${profileData.firstName} ${profileData.lastName}`,
                email: profileData.email
            }
        }
    }
})

Here is a snippet of my backend Express JavaScript code:

// this is the setup intent that gets passed to the frontend
const setupIntent = await stripeClient.setupIntents.create({
    customer: customerID,
    payment_method_types: ['us_bank_account']
})

Incisor Arc TweenType Not Functioning as Expected

I’m working with the Incisor framework and trying to implement a parabolic trajectory using the Arc TweenType. However, I’m encountering an issue where the white box only moves along the x-axis without following the intended arc.

I have the following code:

this.graphicObject = new GraphicObject( nc.graphicAssets.WhiteBox, nc.mainScene, "WhiteBox" );
     
let swooper = this.graphicObject.position.swoop.x( 200, 5, nc.tweenTypes.Arc, this, "swoopComplete" );
swooper.controllers.Arc.arcY = 200;
        
this.swoopComplete = function() {
    console.log("swoop complete");
}

Issue:

  • The white box moves straight along the x-axis to position 200, but no arc is applied.
  • No exceptions are thrown.

What I’ve tried:

  • I imported and attempted to configure the Arc TweenType, but it seems like it is being ignored.
  • The white box should follow a parabolic trajectory along the x-axis, but it moves in a straight line instead.

Could anyone help me understand why the Arc TweenType is not being applied properly? Is there a specific configuration or method I’m missing here?

Google Drive API Fetch Query Javascript Syntax

I am struggling with understanding how to put compound queries together for Google Drive API.
As examples, the following fetches all work.

'https://www.googleapis.com/drive/v3/files?q=mimeType+=+'application/vnd.google-apps.folder'
'https://www.googleapis.com/drive/v3/files?fields=files(id,name)'
'https://www.googleapis.com/drive/v3/files?nextPageToken'
'https://www.googleapis.com/drive/v3/files?pageSize=200'

Can/How do I build a single compound query for all 4 of these ie. fetch 200 records that are folders and give me their id, name and a nextPageToken?

Typehinting issue with property accessorKey on @tanstack/react-table

I’m trying to use the @tanstack/react-table package correctly with Typescript, but I keep running into a problem that I haven’t been able to solve on my own.

The problem has to do with the accessorKey property. I keep trying to somehow find a typesafe way to access this property, but have not yet been successful.

I first look for a particular column that has the custom property primary in it.

const primaryColumn = columns.find((column) => column.meta?.primary === true)

And then I try to access the accessorKey property on primaryColumn, but every time I do I get a typescript error.

TS2339: Property accessorKey does not exist for type ColumnDef<TData, TValue>.
Property accessorKey does not exist on type
ColumnDefBase<TData, TValue> & StringHeaderIdentifier

Here is my column definition in case it is relevant:

const columns: ColumnDef<Organisation>[] = [
    {
        meta: {
            primary: true
        },
        accessorKey: "name",
        header: ({ column }) => (
            <DataTableColumnHeader column={column} title={"Name"} />
        ),
    },
    ...
]

Has anyone encountered the same problem and knows what to do from here?

If there is any information missing or otherwise, please let me know. I will update the post with the additional information as soon as possible.

Angular 18 problem with component logic to save and bring in localstorage

I am creating a component using Angular 18 that shows a list of clients and one of the characteristics of this component is that it must save in the localstorage the status of the applied filters and the number of the page in which it is located so that when the user goes to another page and returns to the list of clients, it shows the information according to the last status saved in the localstorage. My problem is that when I select a page in the pagination and go to another page when returning to the list, the pagination returns to 1 and I cannot find exactly the reason. I have followed up and apparently when initializing the component it loads the data from the localstorage but then they return to 1. I have created a service to bring the data and save it in the localstorage, I would like to find the reason for this behavior. I have been thinking of placing the information loading logic in the constructor but I think it is better to keep it in the NgOnInit end.

this is mi component:

  selector: 'app-cliente-list',
  standalone: true,
  imports: [NgbPaginationModule, ClienteFiltersComponent, NgForOf, NgIf, FaIconComponent],
  providers: [{ provide: ClienteGateway, useClass: ClienteApiService }, ListarClienteUseCase],
  templateUrl: './cliente-list.component.html',
  styleUrls: ['./cliente-list.component.scss']
})
export class ClienteListComponent implements OnInit {

  filtros: any = { nombre: '', tipo_cliente: '', tiene_credito: null };
  clientes: Cliente[] = [];
  count: number = 0;
  current_page: number = 1;

  constructor(
    private readonly listarClienteUseCase: ListarClienteUseCase,
    private readonly toastService: ToastService,
    private readonly modalService: NgbModal,
    private readonly breadcrumbService: BreadcrumbService,
    private readonly route: ActivatedRoute,
    private readonly router: Router,
    private readonly clienteStateService: ClienteStateService
  ) { }

  ngOnInit(): void {
    const { filtros, page } = this.clienteStateService.getState();
    this.filtros = filtros || this.filtros;
    this.current_page = page ?? 1;
    this.loadClientes(this.current_page, this.filtros);
    this.breadcrumbService.addBreadcrumb({
      label: 'Lista de Cliente',
      url: this.route.snapshot.url.join('/'),
      nivel: 2,
    });
  }

  loadClientes(page: number, filtros: any): void {
    this.listarClienteUseCase.execute(page, filtros).subscribe({
      next: (response) => {
        this.clientes = response.results;
        this.count = response.count;
        this.current_page = response.current_page;
        this.clienteStateService.saveState({ filtros: this.filtros, page: this.current_page });
      },
      error: (err) => {
        this.toastService.error('Error al cargar clientes:', err);
      },
    });
  }

  onPageChange(page: number): void {
    this.current_page = page;
    this.clienteStateService.saveState({ filtros: this.filtros, page: this.current_page });
    this.loadClientes(this.current_page, this.filtros);
  }

  verDetalle(clienteId: number): void {
    this.clienteStateService.saveState({ filtros: this.filtros, page: this.current_page });
    this.router.navigate(['/clientes', clienteId]);
  }

  trackByClienteId(index: number, cliente: Cliente): number {
    return cliente.id;
  }

  openFiltrosCliente(content: TemplateRef<any>) {
    const modalRef = this.modalService.open(content, { ariaLabelledBy: 'modal-basic-title', scrollable: true, size: 'lg' });
    modalRef.result.then((filtros: any) => {
      if (filtros) {
        this.filtros = filtros;
        this.current_page = 1;
        this.loadClientes(this.current_page, this.filtros);
      }
    }, (reason) => {
      console.log('Modal cerrado:', reason);
    });
  }
} 

and this is my service


@Injectable({
  providedIn: 'root'
})
export class ClienteStateService {

  private readonly stateKey = 'clienteListState';

  constructor() { }

  saveState(state: { filtros: any; page: number }): void {
    localStorage.setItem(this.stateKey, JSON.stringify(state));
  }

  getState(): { filtros: any; page: number } {
    const storedState = localStorage.getItem(this.stateKey);
    if (storedState) {
      return JSON.parse(storedState);
    }
    return { filtros: { nombre: '', tipo_cliente: '', tiene_credito: null }, page: 1 };
  }

  clearState(): void {
    localStorage.removeItem(this.stateKey);
  }
}

I appreciate any help you can give me, thank you very much.

Vuejs HTML2pdf Content Break in mid way

I’m using the vue-html2pdf library to generate PDFs from dynamic data in my Vue.js application. I’m trying to control page breaks within the PDF, especially for a grid layout where each item is rendered inside a col-md-4 (with multiple items per row). I’m attempting to avoid breaking content in the middle of each item by using page-break-inside: avoid;, but it’s still breaking content incorrectly.
Issue:
The page-break-inside: avoid; CSS property doesn’t seem to work as expected. Even though I am using it to prevent page breaks in the middle of items, the content is still breaking in unexpected places.

I’ve also tried adjusting the :paginate-elements-by-height=”1400″ and other layout options, but they don’t seem to resolve the issue.

What I’ve tried:

Adding page-break-inside: avoid; to prevent content from being split.
Adjusting the pagination and height settings in vue-html2pdf.
Using both :float-layout=”false” and :manual-pagination=”false” to prevent automatic page breaks.
Testing with various amounts of content and different screen sizes.

<vue-html2pdf
      style="display: none"
      :show-layout="true"
      :float-layout="false"
      :enable-download="true"
      :preview-modal="false"
      :paginate-elements-by-height="1400"
      :filename="'Bulk_Asset_QR_Codes.pdf'"
      :pdf-quality="4"
      :manual-pagination="false"
      pdf-format="a4"
      pdf-orientation="portrait"
      pdf-content-width="100%"
      ref="html2Pdf"
    >
      <section slot="pdf-content" style="padding: 20px; box-sizing: border-box">
        <div class="p-3 mt-2">
          <div
            class="row"
            style="
              page-break-inside: avoid;
              display: flex;
              justify-content: space-between;
            "
          >
            <div
              class="col-md-4 d-flex justify-content-center mb-4"
              v-for="(item, index) in selectedAssets"
              :key="index"
            >
              <div
                style="
                  border: 1px solid black;
                  padding: 10px;
                  width: 100%;
                  display: flex;
                  flex-direction: column;
                  justify-content: space-between;
                "
              >
                <div
                  class="d-flex justify-content-between align-items-center"
                  style="margin-bottom: 10px"
                >
                  <div>
                    <VueQRCodeComponent
                      :size="90"
                      :text="item.QR_CODE"
                    ></VueQRCodeComponent>
                  </div>
                  <div>
                    <img
                      src="@/assets/images/TPCDT_logo.png"
                      alt="company_logo"
                      style="width: 100px"
                    />
                  </div>
                </div>
                <div class="d-flex" style="justify-content: space-between">
                  <div>
                    <label class="text-primary">Asset Name</label>
                  </div>
                  <div class="d-flex">
                    <label class="text-primary">Asset Code:&nbsp;&nbsp;</label>
                    <p class="mb-0">{{ item.ASSET_CODE }}</p>
                  </div>
                </div>
                <p class="mb-0">
                  {{
                    item.ASSET_NAME.length > 30
                      ? item.ASSET_NAME.substring(0, 30) + "..."
                      : item.ASSET_NAME
                  }}
                </p>
              </div>
            </div>
          </div>
        </div>
      </section>
    </vue-html2pdf>

How to fix Issue with select in jquery horizontal scrolling div?

jquery horizontal scrolling div > inside select
problem: select dont open (because return false in mousedown event)
if delete return false – select open when dragging.
I need click and drag scrolling like touch devices: if click on select – select open, if dragging on select – select not open. Help fix it.

Demo: https://codepen.io/maxbeat/pen/LEPXjoo

$('.scroll-container').mousedown(function (e) {
        $(this)
            .data('down', true)
            .data('x', e.clientX)
            .data('scrollLeft', this.scrollLeft)
        return false; // предотвращаем стандартное поведение
    }).mousemove(function (e) {
        if ($(this).data('down') == true) {
            this.scrollLeft = $(this).data('scrollLeft') + $(this).data('x') - e.clientX;
            $(this).addClass('dragging');
        }
    }).mouseup(function (e) {
        $(this)
            .data('down', false)
            .removeClass('dragging');
    });
    // если мышка соскакивает с блока, чтобы не залипала потом на нем
    $(document).mouseup(function (e) {
        $('.scroll-container')
        .data('down', false)
        .removeClass('dragging');
    });
.scroll-container {
  overflow-x: auto;
  padding: 10px;
  width: 800px;
  cursor: grab;
}
.dragging {
  cursor: grabbing;
}
.list {
  display: flex;
  gap: 20px;
  width: max-content;
}
.button {
  font-family: arial, helvetica, serif;
  background: #ccc;
  width: fit-content;
  padding: 15px;
  border-radius: 10px;
}
<div class="scroll-container">
  <div class="list">
    <div class="button">
      <div class="button__title">Статус</div>
      <div class="button__select">
        <select class="ui__button-select" required>
                    <option value="1" selected>Подтвержден</option>
                    <option value="3">Требует внимания</option>
                    <option value="4">Отклонен</option>
                </select>
      </div>
    </div>
    <div class="button">
      <div class="button__title">Статус</div>
      <div class="button__select">
        <select class="ui__button-select" required>
                    <option value="1" selected>Подтвержден</option>
                    <option value="3">Требует внимания</option>
                    <option value="4">Отклонен</option>
                </select>
      </div>
    </div>
    <div class="button">
      <div class="button__title">Статус</div>
      <div class="button__select">
        <select class="ui__button-select" required>
                    <option value="1" selected>Подтвержден</option>
                    <option value="3">Требует внимания</option>
                    <option value="4">Отклонен</option>
                </select>
      </div>
    </div>
    <div class="button">
      <div class="button__title">Статус</div>
      <div class="button__select">
        <select class="ui__button-select" required>
                    <option value="1" selected>Подтвержден</option>
                    <option value="3">Требует внимания</option>
                    <option value="4">Отклонен</option>
                </select>
      </div>
    </div>
    <div class="button">
      <div class="button__title">Статус</div>
      <div class="button__select">
        <select class="ui__button-select" required>
                    <option value="1" selected>Подтвержден</option>
                    <option value="3">Требует внимания</option>
                    <option value="4">Отклонен</option>
                </select>
      </div>
    </div>
    <div class="button">
      <div class="button__title">Статус</div>
      <div class="button__select">
        <select class="ui__button-select" required>
                    <option value="1" selected>Подтвержден</option>
                    <option value="3">Требует внимания</option>
                    <option value="4">Отклонен</option>
                </select>
      </div>
    </div>
    <div class="button">
      <div class="button__title">Статус</div>
      <div class="button__select">
        <select class="ui__button-select" required>
                    <option value="1" selected>Подтвержден</option>
                    <option value="3">Требует внимания</option>
                    <option value="4">Отклонен</option>
                </select>
      </div>
    </div>
    <div class="button">
      <div class="button__title">Статус</div>
      <div class="button__select">
        <select class="ui__button-select" required>
                    <option value="1" selected>Подтвержден</option>
                    <option value="3">Требует внимания</option>
                    <option value="4">Отклонен</option>
                </select>
      </div>
    </div>
    <div class="button">
      <div class="button__title">Статус</div>
      <div class="button__select">
        <select class="ui__button-select" required>
                    <option value="1" selected>Подтвержден</option>
                    <option value="3">Требует внимания</option>
                    <option value="4">Отклонен</option>
                </select>
      </div>
    </div>
    <div class="button">
      <div class="button__title">Статус</div>
      <div class="button__select">
        <select class="ui__button-select" required>
                    <option value="1" selected>Подтвержден</option>
                    <option value="3">Требует внимания</option>
                    <option value="4">Отклонен</option>
                </select>
      </div>
    </div>
    <div class="button">
      <div class="button__title">Статус</div>
      <div class="button__select">
        <select class="ui__button-select" required>
                    <option value="1" selected>Подтвержден</option>
                    <option value="3">Требует внимания</option>
                    <option value="4">Отклонен</option>
                </select>
      </div>
    </div>
  </div>
</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>

Extension Development: read and write inconsistencies in background.js

class Tab {
    constructor(id, title, url, tabFavicon, lastAccessed) {
        this.id = id;
        this.title = title;
        this.url = url;
        this.tabFavicon = tabFavicon;
        this.lastAccessed = lastAccessed;
    }
}

chrome.runtime.onInstalled.addListener(() => {
    console.log("Extension installed!");

    const storedTabs = [];
    chrome.tabs.query({}, (tabs) => {
        tabs.forEach((tab) => {
            storedTabs.push(new Tab(tab.id, tab.title, tab.url, tab.favIconUrl, tab.lastAccessed));
        });

        chrome.storage.sync.set({ tabs: storedTabs });
        chrome.storage.sync.set({ inactivityThreshold: { hours: 0, minutes: 30 } });
    });
});

// Handle tab creation
chrome.tabs.onCreated.addListener((tab) => {
    chrome.storage.sync.get("tabs", (data) => {
        const storedTabs = data.tabs || [];
        storedTabs.push(new Tab(tab.id, tab.title || "New Tab", tab.url || "", tab.favIconUrl || "", tab.lastAccessed));

        console.log("Tabs before ", storedTabs);
        chrome.storage.sync.set({ tabs: storedTabs }, () => {
            console.log("Tabs after ", storedTabs);
        });
    });
});

// Handle tab updates
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
    if (tab.status === "complete") {
        chrome.storage.sync.get("tabs", (data) => {
            const storedTabs = data.tabs || [];
            const updatedTabs = storedTabs.map((storedTab) => {
                if (storedTab.id === tabId) {
                    return new Tab(tab.id, tab.title || storedTab.title, tab.url || storedTab.url, tab.favIconUrl || storedTab.tabFavicon, tab.lastAccessed);
                }
                return storedTab;
            });

            chrome.storage.sync.set({ tabs: updatedTabs }, () => {
                console.log("Tabs updated: ", updatedTabs);
                console.log("Tab updated: ", tab);
            });
        });
    }
});

chrome.tabs.onActivated.addListener((activeInfo) => {
    chrome.tabs.get(activeInfo.tabId, (tab) => {
        chrome.storage.sync.get("tabs", (data) => {
            const storedTabs = data.tabs || [];
            const updatedTabs = storedTabs.map((storedTab) => {
                if (storedTab.id === tab.id) {
                    return { ...storedTab, lastAccessed: tab.lastAccessed };
                }
                return storedTab;
            });

            chrome.storage.sync.set({ tabs: updatedTabs });
        });
    });
});

// Handle tab removal
chrome.tabs.onRemoved.addListener((tabId) => {
    chrome.storage.sync.get("tabs", (data) => {
        const storedTabs = data.tabs || [];
        const remainingTabs = storedTabs.filter((storedTab) => storedTab.id !== tabId);

        chrome.storage.sync.set({ tabs: remainingTabs }, () => {
            console.log("Tab removed: ", tabId);
            console.log(remainingTabs);
        });
    });
});

// Get inactivity threshold
function getInactivityThreshold() {
    return new Promise((resolve) => {
        chrome.storage.sync.get("inactivityThreshold", (data) => {
            const inactivityThreshold = data.inactivityThreshold || { hours: 0, minutes: 30 };
            const { minutes, hours } = inactivityThreshold;
            resolve((minutes + hours * 60) * 60000);
        });
    });
}

// Setup interval
(async function setupInterval() {
    const intervalTime = await getInactivityThreshold();

    setInterval(() => {
        // Add your periodic code here
    }, intervalTime);
})();

Problem: when the extension is installed initially the whole event of reading tabs and writing them to the storage goes smooth,

Case A: but as soon as I create a new tab, like actually clicking on the + button to create a new tab, the event onCreated is fired but the chrome.storage.sync.set does not write the updated tab array to the storage.

Case B: this issue does not arise when I do “Open in new tab” it works fine and the new tab is added and then subsequently updated to new url once the status is complete.

Question: I want to know that why does CASE A does not work but CASE B does?

PS: I implemented an in-memory cache that solved the issue, but still my question remains the same? why does it work in b and not in a?

Caret doesn’t follow text-indent after deleting element in

HTML:

<h1 placeholder="Text Here" contenteditable="true"></h1>

CSS:

h1 {
  border: 1px black solid;
  outline: none;
  text-indent: 36px;
}

h1:empty::before {
  content: attr(placeholder);
  color: grey;
}

JS:

const h1 = document.querySelector('h1');

h1.addEventListener('input', () => {
  if (h1.textContent !== '') return;
  
  let elemToDel = [];
    
  for (let i of h1.children) {
    elemToDel.push(i);
  }

  elemToDel.forEach(i => i.remove());
});

https://jsfiddle.net/5fnvztsg/

The JS code is there to remove remaining newlines when h1.textContent is empty, so that :empty::before gets applied.

However, pressing enter when there is no text present (except for the ::before) causes the caret to seemingly ignore the text-indent, though typing anything then pressing enter, then deleting the newline and typed text seems to put it in the right place again.

I wrote this:

const caretPos = window.getSelection();
const range = document.createRange();

range.selectNodeContents(h1);
range.collapse(false);

caretPos.removeAllRanges();
caretPos.addRange(range);

right after “elemToDel.forEach(i => i.remove());” but it didn’t seem to work.

I also tried:

h1.textContent = ' ';

after “elemToDel.forEach(i => i.remove());” which fixed the caret issue, but at the cost of removing the ::before.

Touchpad disable pinch zoom but allow scroll – JavaScript

I’m developing an online application that relies on a specific zooming feature, as part of its functionality.

Some devices – like laptops – use a touchpad, where you can both scroll and pinch to zoom using just two fingers.
Since this was a zooming-based web app, I needed to disable the user gesture of pinching to zoom into the actual content of the page.

What I’ve Tried Already

After doing a lot of research, I finally found an answer that would prevent users from pinch-zooming. (Shown below)

JavaScript
window.addEventListener('wheel', e => {
    e.preventDefault()
}, {passive: false})

The Problem

This approach works well. However, the current issue is that – although this code does indeed disable pinch-zooming on a touchpad – it also disables standard vertical scrolling when using two fingers. As a result, for devices with touchpads major parts of my application are completely hidden from view / inaccessible.


I need a JavaScript solution that disables pinch-zooming but preserves vertical scrolling functionality on touchpads.

Please note: I am aware that it is generally discouraged to disable pinch-zoom on a webpage, but it is necessary for this application, as explained before.

Thanks