Prisma Many to Many relationship with at least one value

I use Prisma and Postgresql for a project to store and manage recipes.

I am struggling to implement a Many to Many relationship where I can constrain one side to require at least one value for the relation field.

Recipes can have one or more aromas. Aromas can be included in zero or more recipes.

I use an explicit relation table because I need to store additional data in it (the quantity for each aroma in a recipe).

The Recipe, Aroma and relation models are the following:

model Recipe {
  id          Int              @id @default(autoincrement())
  name        String
  base        String?          @default("50/50")
  description String?          @default("aucune description")
  rating      Int?             @default(1)
  aromas      RecipeToAromas[]
}

model Aroma {
  id      Int              @id @default(autoincrement())
  name    String
  brand   Brand?           @relation(fields: [brandId], references: [id])
  brandId Int?             @default(1)
  recipes RecipeToAromas[]

  @@unique([name, brandId], name: "aromaIdentifier")
}

model RecipeToAromas {
  id         Int    @id @default(autoincrement())
  recipeId   Int
  aromaId    Int
  quantityMl Int
  recipe     Recipe @relation(fields: [recipeId], references: [id])
  aroma      Aroma  @relation(fields: [aromaId], references: [id])
}

I want to constrain recipes to have at least one aroma.

By definition Many to Many defines zero to many relationship.

I thought about solving the problem with adding an additional One to Many relationship between Recipe and Aroma.

That would imply adding an additional aroma field in Recipe to store the one aroma that is required (and rename aromas field to additionalAromas to avoid confusion) :

model Recipe {
  id          Int              @id @default(autoincrement())
  name        String
  base        String?          @default("50/50")
  description String?          @default("aucune description")
  rating      Int?             @default(1)
  aromas      RecipeToAromas[]
  aroma       Aroma            @relation(fields: [aromaId], references: [id])
  aromaId     Int
}

And adding a recipe field in Aroma as it required to establish the relation :

model Aroma {
  id      Int              @id @default(autoincrement())
  name    String
  brand   Brand?           @relation(fields: [brandId], references: [id])
  brandId Int?             @default(1)
  recipes RecipeToAromas[]
  recipe  Recipe[]

  @@unique([name, brandId], name: "aromaIdentifier")
}

But that feels wrong as I will have duplicates : recipes and recipe fields in Aroma would store identical data.

I could of course workaround the problem by just relying on validation in mutation functions and user input. And probably try to add a layer of safety with types as I am using typescript.
But I feel like it would make the database brittle and is prone to error especially if I have to collaborate with other devs, or even use the database in a different projet.

I could not find any resource covering a similar situation, and of course I have spend a lot of time searching and re-reading the documentation.

I am new to prisma (started yesterday) and I dont have too much experience with RDBMS, so it feels like I am missing something.

I keep getting this error : Uncaught TypeError: Cannot read properties of undefined (reading ‘image’) at PromptCard (PromptCard.jsx:37:43)

enter image description here

This is my componentsPromptCard.jsx code :-

"use client";

import { useState } from "react";
import Image from "next/image";
import { useSession } from "next-auth/react";
import { usePathname, useRouter } from "next/navigation";

const PromptCard = ({ post, handleEdit, handleDelete, handleTagClick }) => {
    const { data: session } = useSession();
    const pathName = usePathname();
    const router = useRouter();

    const [copied, setCopied] = useState("");

    const handleProfileClick = () => {
        console.log(post);

        if (post.creator._id === session?.user.id) return router.push("/profile");

        router.push(`/profile/${post.creator._id}?name=${post.creator.username}`);
    };

    const handleCopy = () => {
        setCopied(post.prompt);
        navigator.clipboard.writeText(post.prompt);
        setTimeout(() => setCopied(false), 3000);
    };

    return (
        <div className='prompt_card'>
            <div className='flex justify-between items-start gap-5'>
                <div
                    className='flex-1 flex justify-start items-center gap-3 cursor-pointer'
                    onClick={handleProfileClick}
                >
                    <Image
                        src={post.creator.image}
                        alt='user_image'
                        width={40}
                        height={40}
                        className='rounded-full object-contain'
                    />

                    <div className='flex flex-col'>
                        <h3 className='font-satoshi font-semibold text-gray-900'>
                            {post.creator.username}
                        </h3>
                        <p className='font-inter text-sm text-gray-500'>
                            {post.creator.email}
                        </p>
                    </div>
                </div>

                <div className='copy_btn' onClick={handleCopy}>
                    <Image
                        src={
                            copied === post.prompt
                                ? "/assets/icons/tick.svg"
                                : "/assets/icons/copy.svg"
                        }
                        alt={copied === post.prompt ? "tick_icon" : "copy_icon"}
                        width={12}
                        height={12}
                    />
                </div>
            </div>

            <p className='my-4 font-satoshi text-sm text-gray-700'>{post.prompt}</p>
            <p
                className='font-inter text-sm blue_gradient cursor-pointer'
                onClick={() => handleTagClick && handleTagClick(post.tag)}
            >
                {post.tag}
            </p>

            {session?.user.id === post.creator._id && pathName === "/profile" && (
                <div className='mt-5 flex-center gap-4 border-t border-gray-100 pt-3'>
                    <p
                        className='font-inter text-sm green_gradient cursor-pointer'
                        onClick={handleEdit}
                    >
                        Edit
                    </p>
                    <p
                        className='font-inter text-sm orange_gradient cursor-pointer'
                        onClick={handleDelete}
                    >
                        Delete
                    </p>
                </div>
            )}
        </div>
    );
};

export default PromptCard;

I tried updating my NextJS to the latest version, also checked all the environment variables and all seem to work perfectly. The code was working well and good but once I deployed it using Vercel, this error showed up.
I tried running in my localhost and the same error popped up. This wasn’t there before I deployed it to vercel.

Application error: a client-side exception has occurred (see the browser console for more information).

This error shows on the browser when I deployed it.

Here’s the website link : https://quasar-prompts.vercel.app/

Here’s the Github link : https://github.com/AnanteshG/Quasar

If anybody knows how to fix it let me know please. Thanks in Advance!

How do I make this insult generator work?

I have a problem where I’m trying to generate insults, or just use this as the base for a game and other projects, but when I hit the generate insult button, it doesn’t do anything.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>The Unfiltered Insult Generator</title>
  <style>
        fieldset {
            margin-bottom: 1em;
        }
        legend {
            font-weight: bold;
        }
        .language-select {
            display: inline-block;
        }
        .language-select ol {
            list-style-type: none;
            padding: 1;
        }
        .language-select label {
            display: block;
        }
        .language-select input[type="checkbox"] {
            margin-right: 0.5em;
        }
        .language-select input[type="text"] {
            margin-top: 1em;
        }
        .srOnly {
           position: absolute; 
           width: 1px;
           height: 1px;
           padding: 0;
           margin: -1px;
           overflow: hidden;
           clip: rect(0,0,0,0); 
           border: 0;
        }
  </style>
</head>
<body>
  <script>
          function getRandom() {
            return Math.floor(Math.random() * 100);
        }

        function generateInsult() {
            let name = document.getElementById('name').value;
            let num = getRandom();
            let insult;

            if (num === 99) {
                            insult = name + " is a lucky ducky! 99, dam!";
                             id99
                        } else if (num === 98) {
                            insult = name + " was so close to success. But I guess you're just a failure in life.";
                             id98
                        } else if (num === 97) {
                            insult = name + " is kinda stupid.";
                             id97
                        } else if (num === 96) {
                            insult ="Hey" + name + "turn it upside down.";
                             id96
                        } else if (num === 95) {
                            insult = num + " is literally the most boring number you could have gotten. Go try again.";
                             id95
                        } else if (num === 94) {
                            insult = "Hey " + name + ", Can I have the name of your hair salon? I need to know where not to go.";
                             id94
                        } else if (num === 93) {
                            insult = name + ", You are the human equivalent of a participation trophy.";
                             id93
                        } else if (num === 92) {
                            insult = name + ", you have a face for radio.";
                             id92
                        } else if (num === 91) {
                            insult = name + ", whatever kind of look you were aiming for, you missed.";
                             id91
                        } else if (num === 90) {
                            insult = name + ", I smell something burning. Are you trying to think again?";
                             id90
                        } else if (num === 89) {
                            insult = name + ", you’re like a lighthouse in a desert: bright but not very useful.";
                             id89
                        } else if (num === 88) {
                            insult = name + ", you’re as useless as the “ueue” in “queue!";
                             id88
                        } else if (num === 87) {
                            insult = name + ", whatever is eating you must be suffering terribly.";
                             id87
                        } else if (num === 86) {
                            insult = name + ",  if you ever had a thought, it would die of loneliness.";
                             id86
                        } else if (num === 85) {
                            insult = name + ", this is why the gene pool needs a lifeguard.";
                             id85
                        } else if (num === 84) {
                            insult = name + ", you have the charisma of a wet sock.";
                             id84
                        } else if (num === 83) {
                            insult = name + ", I don’t understand, but I also don’t care, so it works out nicely.";
                             id83
                        } else if (num === 82) {
                            insult = name + ", I’ve seen salad that dresses better than you.";
                             id82
                        } else if (num === 81) {
                            insult = name + ", You have the communication skills of an alarm clock.";
                             id81
                        } else if (num === 80) {
                            insult = name + ", honestly, I'm just impressed you could read this.";
                             id80
                        } else if (num === 79) {
                            insult = name + ", no I'm not insulting you, I'm describing you.";
                             id79
                        } else if (num === 78){
                            insult = name + ", your birth certificate is an apology letter from the condom factory.";
                             id78
                        } else if (num === 77){
                            insult = name + ", the only way you'll ever get laid is if you crawl up a chicken's ass and wait.";
                             id77
                        } else if (num === 76) {
                            insult = name + ", my psychiatrist told me I was crazy and I said I want a second opinion. He said okay, you're ugly too.";
                             id76
                        } else if (num === 75) {
                            insult = name + ", you must have been born on a highway because that's where most accidents happen.";
                             id75
                        } else if (num === 74) {
                            insult = name + ", brains aren't everything. In your case they're nothing.";
                             id74
                        } else if (num === 73) {
                            insult = name + ", some babies were dropped on their heads but you were clearly thrown at a wall.";
                             id73
                        } else if (num === 72) {
                            insult = name + ", you can walk, but can you talk?";
                             id72
                        } else if (num === 71) {
                            insult = name + ", I'd slap you, but that would be animal abuse.";
                             id71
                        } else if (num === 70) {
                            insult = name + ", stop trying to be a smart ass, you're just an ass.";
                             id70
                        } else if (num === 69) {
                            insult = "Damn, you just got the best number!";
                             id69
                        } else if (num === 68) {
                            insult = "One off, get better at life.";
                             id68
                        } else if (num === 67) {
                            insult = "Why was six scared of seven? Because seven ate nine!";
                             id67
                        } else if (num === 66) {
                            insult = name + ", why don't you slip into something more comfortable... like a coma.";
                             id66
                        } else if (num === 65) {
                            insult = name + ", you get ten times more girls than me? ten times zero is zero...";
                             id65
                        } else if (num === 64) {
                            insult = name + ", You're so fat, you could sell shade."
                             id64
                        } else if (num === 63) {
                            insult = name + ", have you been shopping lately? They're selling lives, you should go get one.";
                             id63
                        } else if (num === 62) {
                            insult = name + ", I don't think you understand the concept of 'no eating'.";
                             id62
                        } else if (num === 61) {
                            insult = name + ", that's one small step for me, and a massive one for you.";
                             id61
                        } else if (num === 60) {
                            insult = name + ", I'd like to see things from your point of view but I can't seem to get my head that far up my ass.";
                             id60
                        } else if (num === 59) {
                            insult = name + ", every time I'm next to you, I get a fierce desire to be alone.";
                             id59
                        } else if (num === 58) {
                            insult = name + ", why do you always wear black? Do you like looking like a clown?";
                             id58
                        } else if (num === 57) {
                            insult = name + ", you're so dumb that you got hit by a parked car.";
                             id57
                        } else if (num === 56) {
                            insult = name + ", how did you get here? Did someone leave your cage open?";
                             id56
                        } else if (num === 55) {
                            insult = name + ", I didn’t ask for your opinion.";
                             id55
                        } else if (num === 54) {
                            insult = name + ", pardon me, but you've obviously mistaken me for someone who gives a damn.";
                             id54                          
                        } else if (num === 53) {
                            insult = name + ", don't you have a terribly empty feeling - in your skull";
                             id53
                        } else if (num === 52) {
                            insult = name + ", as an outsider, what do you think of the human race?";
                             id52
                        } else if (num === 51) {
                            insult = name + ", I have neither the time nor the crayons to explain this to you.";
                             id51
                        } else if (num === 50) {
                            insult = name + ", I would agree with you but then we would both be wrong.";
                             id50
                        } else if (num === 49) {
                            insult = name + ", you're so dense, light bends around you.";
                              id49
                        } else if (num === 48) {
                            insult = name + ", you're as useful as a waterproof teabag.";
                             id48
                        } else if (num === 47) {
                            insult = name + ", you're so clumsy, you could trip over a wireless network.";
                             id47
                        } else if (num === 46) {
                            insult = ", you're so slow, you make sloths look like Olympic sprinters.";
                             id46
                        } else if (num === 45) {
                            insult = name + ", you're not completely useless; you can always serve as a bad example.";
                             id45
                        } else if (num === 44) {
                            insult = name + ", you're so gullible, I could sell you air and you'd buy it.";
                             id44
                        } else if (num === 43) {
                            insult = name + ", your family tree must be a cactus, because everyone on it is a prick.";
                             id43
                        } else if (num === 42) {
                            insult = name + ", you're not just a clown; you're the entire circus.";
                             id42
                        } else if (num === 41) {
                            insult = name + ", your agility is comparable to a turtle on a trampoline.";
                             id41
                        } else if (num === 40) {
                            insult = name + ", you have the problem-solving skills of a confused penguin in a desert";
                             id40
                        } else if (num === 39) {
                            insult = name + ", you look like you were drawn with my left hand";
                             id39
                        } else if (num === 38) {
                            insult = name + ", I do not consider you a vulture. I consider you something a vulture would eat."
                             id38
                        } else if (num === 37) {
                            insult = name + ", what do you think of that, Mr. Pajama-Wearing, Basket-Face, Slipper-Wielding, Clipe-Dreep-Bauchle, Gether-Uping-Blate-Maw, Bleathering, Gomeril, Jessie, Oaf-Looking, Stauner, Nyaff, Plookie, Shan, Milk-Drinking, Soy-Faced Shilpit, Mim-Moothed, Sniveling, Worm-Eyed, Hotten-Blaugh, Vile-Stoochie, Callie-Breek-Tattie?";
                             id37
                        } else if (num === 36) {
                            insult = "Your mother was a broken down tub of junk with more gentlemen callers than the operator.";
                             id36
                        } else if (num === 35) {
                            insult = "White people are just untitled word documents.";
                             id35
                        } else if (num === 34) {
                            insult = "Mirrors can't talk. Lucky for you, they can't laugh either.";
                             id34
                        } else if (num === 33) {
                            insult = "Only 1 in 100 people get this on their first try. You aren't lucky. You're just the hundreth person.";
                             id33
                        } else if (num === 32) {
                            insult = "I’ll never forget the first time we met. But I’ll keep trying.";
                             id32
                        } else if (num === 31) {
                            insult = "Here are the first hundred digits of pi! You're welcome: 3.1415926535 8979323846 2643383279 5028841971 6939937510 5820974944 5923078164 0628620899 8628034825 3421170679";
                             id31
                        } else if (num === 30) {
                            insult = "Honestly, does " + name + " even appreciate my work?";
                             id30
                        } else if (num === 29) {
                            insult = "The number 29 is fine. But are you?";
                             id29
                        } else if (num === 28) {
                            insult = "Did the mental hospital test too many drugs on you today?";
                             id28
                        } else if (num === 27) {
                            insult = name + ", I thought of you today. It reminded me to take out the trash.";
                             id27
                        } else if (num === 26) {
                            insult = name +", somewhere out there is a tree tirelessly producing oxygen for you. You owe it an apology.";
                             id26
                        } else if (num === 25) {
                            insult = name + ", you just might be why the middle finger was invented in the first place.";
                             id25
                        } else if (num === 24) {
                            insult = name + ", if I had a face like yours, I would sue my parents.";
                             id24
                        } else if (num === 23) {
                            insult = name + ", if I wanted to kill myself, I would climb to your ego and jump to your IQ.";
                             id23
                        } else if (num === 22) {
                            insult = "Phew, I'm getting tired of typing all those insults. Why don't you help me out a little, and tell me your personality so that I can make more.";
                             id22
                        } else if (num === 21) {
                            insult = name + ", don’t be ashamed of who you are. That’s your parent’s job.";
                             id21
                        } else if (num === 20) {
                            insult = name + ", you are proof that evolution can go in reverse.";
                             id20
                        } else if (num === 19) {
                            insult = name + ", Isn’t it dangerous to use your whole vocabulary in one sentence?";
                             id19
                        } else if (num === 18) {
                            insult = name + ", if I had a dollar for every time you said something smart, I’d be broke.";
                             id18
                        } else if (num === 17) {
                            insult = "People clap when they see you. They clap their hands over their eyes.";
                             id17
                        } else if (num === 16) {
                            insult = name + ", I bet your parents change the subject when their friends ask about you.";
                             id16
                        } else if (num === 15) {
                            insult = "I find the fact that you’ve lived this long both surprising and disappointing.";
                             id15
                        } else if (num === 14) {
                            insult = "You should carry a plant around with you to replace the oxygen you waste.";
                             id14
                        } else if (num === 13) {
                            insult = "If you were the light at the end of the tunnel, I’d turn back around.";
                             id13
                        } else if (num === 12) {
                            insult = "I don’t know what your problem is, but I’m guessing it’s hard to pronounce.";
                             id12
                        } else if (num === 11) {
                            insult = "You see that door? I want you on the other side of it.";
                             id11
                        } else if (num === 10) {
                            insult = "Final 10, I can see the light. Oh wait, you're on the other side. Back we go!";
                             id10
                        } else if (num === 9) {
                            insult = "You look like a person in their 80's, but you act like someone who's 9.";
                             id9
                        } else if (num === 8) {
                            insult = "If I had a dollar for everytime you said something smart I would be broke."; 
                             id8
                        } else if (num === 7) {
                            insult = name + ", may both sides of your pillow be uncomfortably warm.";
                             id7
                        } else if (num === 6) {
                            insult = "This is the most useless number you could have gotten.";
                             id6
                        } else if (num <= 5) {
                            insult = "You had a 5% chance to get this number. Good job!";
                             id5_1
                        } else {
                             return insult;  
                        }
        }

  </script> 
  <h1><script>generateInsult(<!----->Insert Name Here</!------>)</script></h1>
</body>
</html>

I tried multiple different ways, like using getElementId, but it wouldn’t work. I was expecting a header to appear when I push generate insult, but nothing happens. (btw you can use these insults if you want)

how to use sql and php to fetch data for a cart page [closed]

how can I using the GET super global in PHP to fetch the id to create a Cart page

to get the id and of a particular product when clicked and when it clicked I want to fetch products 1 at a time so the image the price the description will sure when another product is clicked I want the price to be added together so the users can know the amount they are using to purchase their product

Unwanted Popup Text on my website Footer After System Restore [closed]

when you scroll at the bottom of my site’s footer there is a green writing**” write own HTML code”** my website earlier didn’t have the popup. However, after doing a system restore sometimes back, they appeared. Even my theme seller couldn’t help. Can you help please. PS. it’s not plugin related at all. thanks in advance

disabling all the plugins but it didnt work.

Implementing Recursive Nested Tables with Dynamic Data in Angular Using DevExtreme Master-Detail Component

The problem involves implementing a dynamic and potentially infinitely recursive nested table structure in an Angular application using the DevExtreme Master-Detail component. The main challenges include:

Dynamic Data: The data structure for the tables is dynamic, meaning the columns and rows can differ every time the data is loaded. This requires a flexible approach to generating table structures and content.

Variable Nesting: Some rows may contain nested tables, while others do not. Furthermore, the depth of nesting can vary, with some rows containing multiple levels of nested tables.

I need to create a recursive Angular component or find a solution to be capable of rendering a table with nested tables based on the provided data, handling the dynamic creation of columns, and ensuring that the nested data is correctly displayed and managed using the Master-Detail component of DevExtreme.

here is my current implementation. but the problem with this solution is that I create a new component for each level of nesting and pass the data.

**Main Table : **

 import { Component} from '@angular/core';

@Component({
  selector: 'app-hierachy-table',
  template:`<dx-data-grid id="gridContainer" [dataSource]="employees" keyExpr="ID" [showBorders]="true" [allowColumnReordering]="true">
  <dxo-master-detail [enabled]="true" [template]="employees"></dxo-master-detail>
  <div *dxTemplate="let employee of employees">
      <employee-detail [key]="employee.key"></employee-detail>
  </div>
</dx-data-grid>`,
})
export class HierachyTableComponent
{
  employees: any = [{
    ID: 1,
    Prefix: 'Mr.',
    FirstName: 'John',
    LastName: 'Heart',
    Position: ['CEO', 'CTFO'],
    State: 'California',
    BirthDate: '1964/03/16',
  },
  {
    ID: 2,
    Prefix: 'Mrs.',
    FirstName: 'Olivia',
    LastName: 'Peyton',
    Position: 'Sales Assistant',
    State: 'California',
    BirthDate: '1981/06/03',
  },
  {
    ID: 3,
    Prefix: 'Mr.',
    FirstName: 'Robert',
    LastName: 'Reagan',
    Position: 'CMO',
    State: 'Arkansas',
    BirthDate: '1974/09/07',
  },
  {
    ID: 4,
    Prefix: 'Ms.',
    FirstName: 'Greta',
    LastName: 'Sims',
    Position: 'HR Manager',
    State: 'Georgia',
    BirthDate: '1977/11/22',
  },
  {
    ID: 5,
    Prefix: 'Mr.',
    FirstName: 'Brett',
    LastName: 'Wade',
    Position: 'IT Manager',
    State: 'Idaho',
    BirthDate: '1968/12/01',
  },
  {
    ID: 6,
    Prefix: 'Mrs.',
    FirstName: 'Sandra',
    LastName: 'Johnson',
    Position: 'Controller',
    State: 'Utah',
    BirthDate: '1974/11/15',
  },
  {
    ID: 7,
    Prefix: 'Mr.',
    FirstName: 'Kevin',
    LastName: 'Carter',
    Position: 'Shipping Manager',
    State: 'California',
    BirthDate: '1978/01/09',
  }];
  constructor() { }

}

First Level Nesting Component:

@Component({
  selector: 'employee-detail',
  template: `<dx-data-grid [dataSource]="tasksDataSource" [showBorders]="true" [columnAutoWidth]="true">
  <dxo-master-detail [enabled]="true" [template]="employeeDetails"></dxo-master-detail>
  <div *dxTemplate="let employee of employeeDetails">
      <employee-development [key]="employee.key"></employee-development>
  </div>
</dx-data-grid>`
})
export class DetailGridComponent implements AfterViewInit
{
  public tasksDataSource: any;

  @Input()
  public key: number | any;
  employeeDetails: any = [
    {
      EmployeeID: 1,
      EmploymentYear: '2012',
      JoinDate: '2012/04/10',
      ContractEnd: '2100/12/31',
      EmploymentStatus: 'Active',
      ProjectsCompleted: 120,
    },
    {
      EmployeeID: 2,
      EmploymentYear: '2012',
      JoinDate: '2014/05/20',
      ContractEnd: '2100/12/31',
      EmploymentStatus: 'Inactive',
      ProjectsCompleted: 60,
    },
    {
      EmployeeID: 3,
      EmploymentYear: '2012',
      JoinDate: '2020/06/15',
      ContractEnd: '2100/12/31',
      EmploymentStatus: 'On Leave',
      ProjectsCompleted: 30,
    },
    {
      EmployeeID: 4,
      EmploymentYear: '2012',
      JoinDate: '2006/07/25',
      ContractEnd: '2100/12/31',
      EmploymentStatus: 'Active',
      ProjectsCompleted: 450,
    },
    {
      EmployeeID: 5,
      EmploymentYear: '2012',
      JoinDate: '2012/08/05',
      ContractEnd: '2100/12/31',
      EmploymentStatus: 'Active',
      ProjectsCompleted: 110,
    },
    {
      EmployeeID: 6,
      EmploymentYear: '2012',
      JoinDate: '2012/09/15',
      ContractEnd: '2100/12/31',
      EmploymentStatus: 'Active',
      ProjectsCompleted: 95,
    },
    {
      EmployeeID: 7,
      EmploymentYear: '2012',
      JoinDate: '2012/10/25',
      ContractEnd: '2100/12/31',
      EmploymentStatus: 'Active',
      ProjectsCompleted: 105,
    },
    {
      EmployeeID: 8,
      EmploymentYear: '2012',
      JoinDate: '2012/11/05',
      ContractEnd: '2100/12/31',
      EmploymentStatus: 'Active',
      ProjectsCompleted: 92,
    },
    {
      EmployeeID: 9,
      EmploymentYear: '2012',
      JoinDate: '2012/12/15',
      ContractEnd: '2100/12/31',
      EmploymentStatus: 'Active',
      ProjectsCompleted: 88,
    },
    {
      EmployeeID: 10,
      EmploymentYear: '2012',
      JoinDate: '2012/04/10',
      ContractEnd: '2100/12/31',
      EmploymentStatus: 'Active',
      ProjectsCompleted: 115,
    }
  ];

  ngAfterViewInit()
  {
    this.tasksDataSource = new DataSource({
      store: new ArrayStore({
        data: this.employeeDetails,
        key: 'EmployeeID',
      }),
      filter: ['EmployeeID', '=', this.key],
    });
  }

}

third Level Nesting:

@Component({
  selector: 'employee-development',
  template: `<dx-data-grid [dataSource]="dataSource" [showBorders]="true" [columnAutoWidth]="true">
  <dxo-master-detail [enabled]="true" [template]="employeeDevelopments"></dxo-master-detail>
  <div *dxTemplate="let development of employeeDevelopments">
      <employee-compansation [key]="development.key"></employee-compansation>
  </div>
</dx-data-grid>`})
export class EmployeeDevelopmentComponent
{
  @Input()
  key: any;
  employeeDevelopments: any = [
    {
      EmployeeID: 1,
      TrainingSessionsAttended: 5,
      SkillsAcquired: ['Project Management', 'Time Management', 'Advanced Excel'],
      LastPerformanceReviewScore: 9,
      NextReviewDate: '2024/04/10',
    },
    {
      EmployeeID: 2,
      TrainingSessionsAttended: 3,
      SkillsAcquired: ['Communication', 'Critical Thinking'],
      LastPerformanceReviewScore: 7,
      NextReviewDate: '2024/05/20',
    },
    {
      EmployeeID: 3,
      TrainingSessionsAttended: 4,
      SkillsAcquired: ['Team Leadership', 'Conflict Resolution', 'Public Speaking'],
      LastPerformanceReviewScore: 8,
      NextReviewDate: '2024/06/15',
    },
    {
      EmployeeID: 4,
      TrainingSessionsAttended: 6,
      SkillsAcquired: ['Strategic Planning', 'Budget Management', 'Negotiation'],
      LastPerformanceReviewScore: 9.5,
      NextReviewDate: '2024/07/25',
    },
    {
      EmployeeID: 5,
      TrainingSessionsAttended: 2,
      SkillsAcquired: ['Data Analysis', 'Critical Thinking'],
      LastPerformanceReviewScore: 8.5,
      NextReviewDate: '2024/08/05',
    },
    {
      EmployeeID: 6,
      TrainingSessionsAttended: 3,
      SkillsAcquired: ['Creative Problem Solving', 'Effective Communication'],
      LastPerformanceReviewScore: 7.5,
      NextReviewDate: '2024/09/15',
    },
    {
      EmployeeID: 7,
      TrainingSessionsAttended: 4,
      SkillsAcquired: ['Project Management', 'Team Motivation', 'Agile Methodologies'],
      LastPerformanceReviewScore: 8,
      NextReviewDate: '2024/10/25',
    },
    {
      EmployeeID: 8,
      TrainingSessionsAttended: 2,
      SkillsAcquired: ['Time Management', 'Stress Management'],
      LastPerformanceReviewScore: 7,
      NextReviewDate: '2024/11/05',
    },
    {
      EmployeeID: 9,
      TrainingSessionsAttended: 5,
      SkillsAcquired: ['Digital Marketing', 'SEO Basics', 'Content Creation'],
      LastPerformanceReviewScore: 8.5,
      NextReviewDate: '2024/12/15',
    },
    {
      EmployeeID: 10,
      TrainingSessionsAttended: 3,
      SkillsAcquired: ['Analytical Thinking', 'Data Presentation'],
      LastPerformanceReviewScore: 9,
      NextReviewDate: '2024/04/10',
    }
  ];

  dataSource: any;
  ngAfterViewInit()
  {
    this.dataSource = new DataSource({
      store: new ArrayStore({
        data: this.employeeDevelopments,
        key: 'EmployeeID',
      }),
      filter: ['EmployeeID', '=', this.key],
    });

  }
}

I am expecting to have a component where it renders the nesting automatically. instead of creating new component for each level of nesting.

“not assignable to type” error for Grommet List with Children in Typescript

I am using grommet component library for my react app and I tried using List component for one of my requirement. I referred grommet documentation :
Example

and accordingly tried using the List component with children using typescript. Below is my code:

    import React from 'react';
    import { Box, List, Tip, Text } from 'grommet';
    import { Gremlin } from 'grommet-icons';
    
    const dataList: { city: string; state: string }[] = [
      { city: 'Boise', state: 'Idaho' },
      { city: 'Fort Collins', state: 'Colorado' },
      { city: 'Bay Area', state: 'California' },
      { city: 'San Diego', state: 'California' },
    ];
    
    const renderListItem = (datum: { city: string; state: string }) => (
      <Tip content={datum.state} dropProps={{ align: { left: 'right' } }}>
        <Box direction="row-responsive" gap="medium" align="center">
          <Gremlin size="large" />
          <Text weight="bold">{datum.city}</Text>
        </Box>
      </Tip>
    );
    
    const Children: React.FC = () => (
      <Box pad="large" height="100%" align="center">
        <List data={dataList} pad="medium" border={false}>
          {renderListItem}
        </List>
      </Box>
    );
    
    export default Children;

But this gives me following error:

Type ‘(datum: { city: string; state: string;}) =>
React.JSX.Element’ is not assignable to type ‘(((…args: any[]) =>
any) & (string | number | boolean | ReactElement<any, string |
JSXElementConstructor> | Iterable | ReactPortal |
null)) | undefined’. Type ‘(datum: { city: string; state:
string;}) => React.JSX.Element’ is not assignable to type ‘((…args:
any[]) => any) & string’.
Type ‘(datum: { city: string; state: string;}) => React.JSX.Element’ is not assignable to type ‘string’.ts(2322)
index.d.ts(46, 3): The expected type comes from property ‘children’
which is declared here on type ‘IntrinsicAttributes &
ListExtendedProps<{ city: string; state: string; }> & { children?:
ReactNode; }’

const renderListItem: (datum: {
    city: string;
    state: string;
}) => React.JSX.Element

I see similar question on SO : Lists with Children in Grommet UI and TypeScript. But I dont see any answers there too. I tried by passing various types but all in vain.
Could any one please throw some light on this ?

Thanks

Export html to docx format and maintain all styles in javascript

How to export html which contains multiple inline styles to word file(docx) ?
I tried below code

const header = "<html xmlns:o='urn:schemas-microsoft-com:office:office' xmlns:w='urn:schemas-microsoft-com:office:word' xmlns='http://www.w3.org/TR/REC-html40'><head><meta charset='utf-8'><title>Export</title></head><body>";
  const footer = '</body></html>';
  const html = header + htmlString + footer;
  const url = `data:application/vnd.ms-word;charset=utf-8,${encodeURIComponent(html)}`;
  const downloadLink = document.createElement('a');
  document.body.appendChild(downloadLink);
  downloadLink.href = url;
  const savedFileName = `${filename}.docx`;
  downloadLink.download = savedFileName;
  downloadLink.click();
  document.body.removeChild(downloadLink);

but it fails with docx(as expected since it requires to map each element to respective .docx format).

custom validation not work in one specific condition how to fix it?

  validateGroups() {                                                        
    const groupsArray = this.theForm.get('groups') as UntypedFormArray;

    groupsArray.controls.forEach((group: AbstractControl) => {
      const startDate = group.get('hg_start_date');
      const endDate = group.get('hg_end_date');

      if (startDate.value && endDate.value && startDate.value > endDate.value) {
        endDate.setErrors({ 'invalidRange': true });
        startDate.setErrors({ 'invalidRange': true });
      } else if (startDate.value == null && endDate.value) {
        startDate.setErrors({ 'fieldRequired': true });
      } else if (startDate.value && endDate.value == null) {
        endDate.setErrors({ 'fieldRequired': true });
      } else {
        startDate.setErrors(null);
        endDate.setErrors(null);
      }
    });
    this.theForm.updateValueAndValidity();
  }
                                    <div *ngIf="data?.uwat_master_type == '10'" class="form-control" fxFlex="auto" fxFlex.gt-lg="25" class="pad12" fxLayout="row" fxLayout.gt-sm="row" fxLayoutGap="10px">
                                        <div fxFlex>
                                            <app-sys-datepicker formControlName="hg_start_date" type="dateAlign" (dateChanged)="validateHolidayGroup($event,i,group)" placeholder="{{ 'start_date' | translate}}"></app-sys-datepicker>
                                            <small class="error-blk" *ngIf="theForm.get('groups').at(i).get('hg_start_date').touched">
                                                <div class="validation-error"
                                                    *ngIf="theForm.get('groups').at(i).get('hg_start_date').hasError('fieldRequired')">
                                                    {{'field_required' | translate}}
                                                </div>
                                            </small>
                                        </div>

                                        <div fxFlex>
                                            <app-sys-datepicker formControlName="hg_end_date" type="dateAlign" (dateChanged)="validateHolidayGroup($event,i,group)" placeholder="{{ 'end_date' | translate}}"></app-sys-datepicker>
                                            <small class="error-blk" *ngIf="theForm.get('groups').at(i).get('hg_end_date').touched">
                                                <div class="validation-error"
                                                    *ngIf="theForm.get('groups').at(i).get('hg_end_date').hasError('invalidRange')">
                                                    {{'invalid_date_range' | translate}}
                                                </div>
                                            </small>
                                            <small class="error-blk" *ngIf="theForm.get('groups').at(i).get('hg_end_date').touched">
                                                <div class="validation-error"
                                                    *ngIf="theForm.get('groups').at(i).get('hg_end_date').hasError('fieldRequired')">
                                                    {{'field_required' | translate}}
                                                </div>
                                            </small>
                                        </div>
                                    </div>

this validation does not working which means when the page loads first time , if i change either of start date or end date only it doesnt work, but if change either of the one first then change another it works!! only invalidRange have problem

i have tried so many debugging its not working

How to create a many to many relationship using mongoose [closed]

I’m following a tutorial on the official MongoDB Developer docs (https://www.mongodb.com/developer/languages/javascript/getting-started-with-mongodb-and-mongoose/) and at the end it says that the blog post could use some comments so that would be a many to many relationship and would be a great practise and use case for many projects but it doesnt explain how to create that. Could someone explain and add the code of how to add comments to a certain blog.

I’ve roamed through many blogs and Youtube video’s but still haven’t found anything that gives me a clear example.

how to use Custom input event primeng angular

I have an angular 16 project
I have a number input, which is validated using formControls
Because we use this input a lot, I have put it in a custom component to make my work easier.

I use primeng inputNumbers

The summary of my custom-input-number code is as follows

html file :

<span class="p-float-label w-100" [formGroup]="form">
  <p-inputNumber
    formControlName="number"
    [disabled]="disabled"
    (onInput)="onChange($event)"
    (onBlur)="onTouched()">
  </p-inputNumber>
  <label for="{{ id }}" class="custom-input-label">{{ label }}</label>
</span>

<small class="unselectable">
  {{textValidators}}
</small>

typescript file :

import { ChangeDetectorRef, Component, EventEmitter, forwardRef, Input, OnInit, Output } from '@angular/core';
import { AbstractControl, ControlValueAccessor, FormControl, FormGroup, NG_VALIDATORS, NG_VALUE_ACCESSOR, ValidationErrors } from '@angular/forms';

@Component({
  selector: 'ngx-custom-input-number',
  templateUrl: './custom-input-number.component.html',
  styleUrls: ['./custom-input-number.component.scss'],
  providers: [
    {
      provide: NG_VALIDATORS,
      useExisting: forwardRef(() => CustomInputNumberComponent),
      multi: true,
    },
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => CustomInputNumberComponent),
      multi: true,
    },
  ],
})
export class CustomInputNumberComponent implements OnInit, ControlValueAccessor {

  form: FormGroup;
  ngModel;
  selectedData = null;
  selectedCountry;
  getValidators;
  textValidators: string = '';

  @Input() mode;
  @Input() label;

  onChange!: (value: string) => void;
  @Output() onBlur = new EventEmitter();

  constructor(private cdr: ChangeDetectorRef) {
    this.ngModel = this.setNgModel;
  }

  ngOnInit(): void {
    this.form = new FormGroup({
      number: new FormControl(this.setDefaultValue),
    });
  }

  onTouched() {
    this.onBlur.emit(this.form.get('number').value)
  }


  writeValue(val: any): void {
    if (val) {
      this.form.get('number').setValue(val, { emitEvent: false });
    } else {
      this.form.get('number').setValue(null);
    }
  }

  registerOnChange(fn: any): void {
    this.onChange = fn;
    this.form.get("number").valueChanges.subscribe(fn);
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }

  setDisabledState?(isDisabled: boolean): void {
    isDisabled
      ? this.form.disable()
      : this.form.enable();
  }

  validate(c: AbstractControl): ValidationErrors | null {...}

}

When I want to use this in another component, it is completely normal
I use it and give it formControlName
like this

  <form [formGroup]="form"
    <ngx-custom-input-number [label]="..." formControlName="number"> </ngx-custom-input-number>
  </form>

Now exactly my problem is when the input value goes to the server That
everything will be ruined The amount that goes to the server

number : {
    formattedValue : '423',
    originalEvent : { isTrusted : true},
    value : 4234
  }

I only want this value and not other things
And my problem is that I can’t change the whole project
I want to change the custom-component as much as possible so that the whole project doesn’t need to be changed

Note: I upgraded the project from angular12 to angular16 and this
problem was not present in angular12

How can I configure different HTTP headers for different routes in Next.js on the client side?

How can I configure different HTTP headers for different routes in Next.js on the client side?

I encountered CORS and SharedBuffer errors in my Next.js application. While I managed to resolve the issues by setting up appropriate headers using the following code, the solution only works when I reload the page. When I navigate between routes on the client side, I still encounter errors.

Specifically:

  • The CORS issue was resolved for the /convert-voice/:path* route.
  • The SharedBuffer error persists for the /my-ai-voices route. Both routes are client-side routes.

I implemented custom HTTP headers for different routes on the client side in my Next.js application. When I reload the page, both routes (/convert-voice/:path* and /my-ai-voices) function as expected, and the CORS and SharedBuffer errors are resolved as intended. However, when I navigate between routes without reloading the page, I encounter the CORS error for the /convert-voice/:path* route and the SharedBuffer error for the /my-ai-voices route. I expected that the configured headers would persist across route changes and prevent these errors from occurring during navigation.

`

import './src/env.mjs';
/** @type {import('next').NextConfig} */

const nextConfig = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'zingzang.s3.eu-west-2.amazonaws.com',
        pathname: '/musicGenerated/**',
      },
      {
        protocol: 'https',
        hostname: 'randomuser.me',
        pathname: '/api/portraits/**',
      },
      {
        protocol: 'https',
        hostname: 'cloudflare-ipfs.com',
        pathname: '/ipfs/**',
      },
      {
        protocol: 'https',
        hostname: 'avatars.githubusercontent.com',
        pathname: '/u/**',
      },
      {
        protocol: 'https',
        hostname: 'picsum.photos',
      },
      {
        protocol: 'https',
        hostname: 'flagcdn.com',
      },
      {
        protocol: 'https',
        hostname: 'utfs.io',
      },
      {
        protocol: 'https',
        hostname: 'images.unsplash.com',
      },
      {
        protocol: 'https',
        hostname: 's3.amazonaws.com',
        pathname: '/redqteam.com/isomorphic-furyroad/public/**',
      },
      {
        protocol: 'https',
        hostname: 'isomorphic-furyroad.s3.amazonaws.com',
      },
      {
        protocol: 'https',
        hostname: 'd2sfqo51tiwost.cloudfront.net',
      },
    ],
  },
  reactStrictMode: false,
  swcMinify: true,
  async headers() {
    return [
      {
        source: '/my-ai-voices',
        headers: [
          {
            key: 'Cross-Origin-Embedder-Policy',
            value: 'require-corp',
          },
          {
            key: 'Cross-Origin-Opener-Policy',
            value: 'same-origin',
          },
          { key: "Access-Control-Allow-Credentials", value: "true" },
          { key: "Access-Control-Allow-Origin", value: "*" },
        ],
      },
      {
        source: '/convert-voice/:path*',
        headers: [
          {
            key: 'Cross-Origin-Embedder-Policy',
            value: 'unsafe-none | require-corp | credentialless',
          },
          {
            key: 'Cross-Origin-Opener-Policy',
            value: 'same-origin',
          },
          { key: "Access-Control-Allow-Credentials", value: "true" },
          { key: "Access-Control-Allow-Origin", value: "*" },
        ],
      },
      {
        source: '/ffmpeg_core_dist/ffmpeg-core.js',
        headers: [
          {
            key: 'Cross-Origin-Embedder-Policy',
            value: 'require-corp',
          },
          {
            key: 'Cross-Origin-Opener-Policy',
            value: 'same-origin',
          },
          { key: "Access-Control-Allow-Credentials", value: "true" },
          { key: "Access-Control-Allow-Origin", value: "*" },
        ],
      },
    ];
  },
};

export default nextConfig;


Is it possible to prevent having multiple selectors when setting an event listener on a button with an image inside?

I have an image inside a button, and I want to listen to clicks on the button.
Since the listener is on the button, then when the click is done on the img, it won’t work:

document.addEventListener("click", (event) => {
  if (event.target.matches(".my-button")) {
    console.log('My button clicked')
  }
});
button {
  background: none;
  color: inherit;
  border: none;
  padding: 1em;
  font: inherit;
  cursor: pointer;
  outline: inherit;
  background-color: #4681f4;
  line-height: 0;
}
<button class="my-button">
  <img src="https://cdn-icons-png.flaticon.com/128/483/483054.png" width="40" height="40"/>
</button>

So I have to add a second selector for the inner img as well:

document.addEventListener("click", (event) => {
  if (event.target.matches(".my-button, .my-button img")) {
    console.log('My button clicked')
  }
});
button {
  background: none;
  color: inherit;
  border: none;
  padding: 1em;
  font: inherit;
  cursor: pointer;
  outline: inherit;
  background-color: #4681f4;
  line-height: 0;
}
<button class="my-button">
  <img src="https://cdn-icons-png.flaticon.com/128/483/483054.png" width="40" height="40"/>
</button>

But that feels redundant and as if I am doing something wrong.
Is there a way to avoid this? I was thinking at first to just use the img without a button element, but that feels like not according to the spec.