Display information box when hovering over Info icon

I’m working on a project where I need to display a little information box when I hover over the i icon (PhInfo). We currently use Vuejs and Vuetify for most styling. What’s the best way to get this result?

<v-col>
  <div>
    <strong> user info </strong>

    <PhInfo class="ml-3" :size="20" />
  </div>
</v-col>

How Define API JSON String Results that Have Different Amounts of Objects [closed]

I am working on a script using data from an API and the API data sometimes has nothing and other times it will different amount of objects under weatherCoded. How do I display that data when their are different amounts?

Full Script:

<script>
 
    window.onload = () => {
 
        const target = document.getElementById('forecast');
        const aeris = new AerisWeather('', '');

        const request = aeris.api().endpoint('forecasts').place('sunnyside,wa').filter('daynight').limit(1).skip(0);
        request.get().then((result) => {
            const data = result.data;
            const { periods } = data[0];
            if (periods) {
                periods.reverse().forEach(period => {
                    const date = new Date(period.dateTimeISO);
                    const precip = period.weatherPrimary || 'Unknown';
                    const pop = period.pop || '0';
                    const code = period.weatherCoded.wx;

                    const html = (`
                        <div class="card">
                            <div class="card-body">
                                
                                ${precip.includes('Smoke') ? `${pop > 15 ? `Areas of Smoke with a
                                Chance of ${code}` : `Areas of Smoke`}` : `${code}`}
                            </div>
                        </div>
                    `);

                    target.insertAdjacentHTML('afterbegin', html);
                });
            }
        }); 
    }
    
</script>

This part I am attempting to display the Objects under weatherCoded.wx

Script Section:

const code = period.weatherCoded.wx;

API JSON Return (Example 1):

"weatherCoded": [
                        {
                            "timestamp": 1757422800,
                            "wx": "S::T,S:L:RW",
                            "dateTimeISO": "2025-09-09T08:00:00-05:00"
                        },
                        {
                            "timestamp": 1757430000,
                            "wx": "C:L:RW,S::T",
                            "dateTimeISO": "2025-09-09T10:00:00-05:00"
                        },
                        {
                            "timestamp": 1757440800,
                            "wx": "C::T,C:L:RW",
                            "dateTimeISO": "2025-09-09T13:00:00-05:00"
                        },
                        {
                            "timestamp": 1757448000,
                            "wx": "S::T,S:L:RW",
                            "dateTimeISO": "2025-09-09T15:00:00-05:00"
                        },
                        {
                            "timestamp": 1757451600,
                            "wx": "C::T,C:L:RW",
                            "dateTimeISO": "2025-09-09T16:00:00-05:00"
                        }
                    ],

API JSON Return (Example 2):

"weatherCoded": [],

API JSON Return (Example 3):

"weatherCoded": [
                        {
                            "timestamp": 1757419200,
                            "wx": "PA::F",
                            "dateTimeISO": "2025-09-09T07:00:00-05:00"
                        },
                        {
                            "timestamp": 1757451600,
                            "wx": ":VL:RW",
                            "dateTimeISO": "2025-09-09T16:00:00-05:00"
                        }
                    ],

It have tried…

const code = period.weatherCoded[1].wx;

Put if there are no weatherCoded, the complete script does not show.

How to implement multiple JSON-LD schema types for a company website?

Body:
I’m adding structured data to my company website which provides digital marketing and web development services.

I want to include both Organization schema and Service schema in JSON-LD format.

Questions:

Can I add multiple schema types (Organization + Services) in one JSON-LD block, or should they be separate?

Will Google accept multiple services inside one block?

How do I avoid errors in Google Search Console when testing my schema?

How do I use the Shopware 6 sw-price-field administration component?

//twig template
<sw-price-field
  v-model="prices"
  :currency="defaultCurrency"
/>
//index.js file

data(){
        return {
            price: [],
            purchasePrices: []
        }
},

computed: {

     defaultCurrency() {
            return Shopware.Store.get('swProductDetail').defaultCurrency;
        },

    prices: {
            get() {
                const prices = {
                    price: this.price,
                    purchasePrices: this.purchasePrices,
                };

                return prices;
            },

            set(value) {
                this.prices = value.price;
                this.purchasePrices = value.purchasePrices;            
            },
        },
}

I got the error message on the browser console, how do I rectify and solve the problem? “An error was captured in current module: TypeError: Cannot convert undefined or null to object at Object.values () at Proxy.get (index-B3Sbv0Gq.js:1:3951) at $T (channel-DBl56l-E.js:9:3233) at get value (channel-DBl56l-E.js:9:15145) at Object.get [as priceForCurrency] (channel-DBl56l-E.js:14:12773) at Object.get (channel-DBl56l-E.js:14:10234) at channel-DBl56l-E.js:14:32085 at Hi (channel-DBl56l-E.js:13:1393) at gn (channel-DBl56l-E.js:13:1456)
at Xo.f.call (channel-DBl56l-E.js:14:31575) errorCaptured @ index-BAMH1QCc.js:1 Hi @ channel-DBl56l-E.js:13 gn @ channel-DBl56l-E.js:13 t.__weh.t.__weh @ channel-DBl56l-E.js:14 d] (channel-DBl56l-E.js:14:12773)”

How to debug only one file in Next.js 15 (Chrome DevTools & VS Code) without stepping into node_modules or other components?

1.Chrome DevTools problem

  • I set up an ignore list (/node_modules/) so it doesn’t step into libraries.
  • But when I add a breakpoint inside a useEffect in my component and step through the code, DevTools jumps into other components in my app that are re-rendered.
  • What I want is to debug only one file (e.g. Sidebar.tsx) without stepping into other files from my own app code.
  • Is there a way in Chrome DevTools to restrict stepping only to a single file?

2.VS Code problem

  • I copied a launch.json config from Next.js docs and tried adding “skipFiles”.
  • I use dedug client-side chrome
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Next.js: debug server-side",
      "type": "node-terminal",
      "request": "launch",
      "command": "npm run dev",
      "skipFiles": [
        "<node_internals>/**",
        "${workspaceFolder}/node_modules/**"
      ]
    },
    {
      "name": "Next.js: debug client-side",
      "type": "chrome",
      "request": "launch",
      "url": "http://localhost:3000"
    },
    {
      "name": "Next.js: debug client-side (Firefox)",
      "type": "firefox",
      "request": "launch",
      "url": "http://localhost:3000",
      "reAttach": true,
      "pathMappings": [
        {
          "url": "webpack://_N_E",
          "path": "${workspaceFolder}"
        }
      ]
    },
    {
      "name": "Next.js: debug full stack",
      "type": "node",
      "request": "launch",
      "program": "${workspaceFolder}/node_modules/next/dist/bin/next",
      "runtimeArgs": ["--inspect"],
      "skipFiles": [
        "<node_internals>/**"
      ],
      "serverReadyAction": {
        "action": "debugWithEdge",
        "killOnServerStop": true,
        "pattern": "- Local:.+(https?://.+)",
        "uriFormat": "%s",
        "webRoot": "${workspaceFolder}"
      }
    }
  ]
}
  • But VS Code still steps into node_modules when I debug.
  • I also want to prevent stepping into other components in my project, and only debug a single file (similar to what I tried with DevTools).

What I tried:

In Chrome DevTools I added /node_modules/ to the ignore list. I also tried ignoring parts of my own src/ folder.

In VS Code I copied the launch.json config from the Next.js documentation and added “skipFiles”: [“<node_internals>/“, “${workspaceFolder}/node_modules/“].

I also tried adding breakpoints only in the file I want to debug.

What I expected:

When stepping through code inside my component (e.g. in a useEffect), I expected the debugger to stay only in that file.

In VS Code I expected skipFiles to fully ignore everything from node_modules.

What actually happened:

In Chrome DevTools it still steps into other React components in my project whenever they re-render, even though I just want to debug one file.

In VS Code it still sometimes steps into code inside node_modules, even though I added it to skipFiles.

The image slider does not switch using the mouse or swipes [closed]

In this slider, written in javascript, the images do not switch either with the mouse or with swipes. What needs to be changed in the code so that all the found images are scrolled when clicking on the arrows and swiping?

The whole code:

var modal = document.getElementById("myModal");
var img = document.getElementsByTagName('img');
var modalImg = document.getElementById('img01');

var currentImage;
    
for (var i = 0; i < img.length - 1; i++) {
  img[i].setAttribute('data', i);
  img[i].onclick = function() {
    modal.style.opacity = "1";
     modal.style.visibility = "visible";

    modalImg.src = this.src;
    currentImage = this;
  }
}

document.getElementsByClassName("close")[0].onclick = function() {
  modal.style.display = "none";
}


let slider = document.querySelector('.slider'),
  sliderList = slider.querySelector('.slider-list'),
  sliderTrack = slider.querySelector('.slider-track'),
  slides = slider.querySelectorAll('.slide'),
  arrows = slider.querySelector('.slider-arrows'),
  prev = arrows.children[0],
  next = arrows.children[1],
  slideWidth = slides[0].offsetWidth,
  slideIndex = 0,
  posInit = 0,
  posX1 = 0,
  posX2 = 0,
  posY1 = 0,
  posY2 = 0,
  posFinal = 0,
  isSwipe = false,
  isScroll = false,
  allowSwipe = true,
  transition = true,
  nextTrf = 0,
  prevTrf = 0,
  lastTrf = --slides.length * slideWidth,
  posThreshold = slides[0].offsetWidth * 0.35,
  trfRegExp = /([-0-9.]+(?=px))/,
  swipeStartTime,
  swipeEndTime,
  getEvent = function() {
    return (event.type.search('touch') !== -1) ? event.touches[0] : event;
  },
  slide = function() {
    if (transition) {
      sliderTrack.style.transition = 'transform .5s';
    }
    
    sliderTrack.style.transform = `translate3d(-${slideIndex * slideWidth}px, 0px, 0px)`;

    prev.classList.toggle('disabled', slideIndex === 0);
    next.classList.toggle('disabled', slideIndex === --currentImage.length);
  },
  swipeStart = function() {
    let evt = getEvent();

    if (allowSwipe) {

      swipeStartTime = Date.now();
      
      transition = true;

      nextTrf = (slideIndex + 1) * -slideWidth;
      prevTrf = (slideIndex - 1) * -slideWidth;

      posInit = posX1 = evt.clientX;
      posY1 = evt.clientY;

      sliderTrack.style.transition = '';

      document.addEventListener('touchmove', swipeAction);
      document.addEventListener('mousemove', swipeAction);
      document.addEventListener('touchend', swipeEnd);
      document.addEventListener('mouseup', swipeEnd);

      sliderList.classList.remove('grab');
      sliderList.classList.add('grabbing');
    }
  },
  swipeAction = function() {

    let evt = getEvent(),
      style = sliderTrack.style.transform,
      transform = +style.match(trfRegExp)[0];

    posX2 = posX1 - evt.clientX;
    posX1 = evt.clientX;

    posY2 = posY1 - evt.clientY;
    posY1 = evt.clientY;

    if (!isSwipe && !isScroll) {
      let posY = Math.abs(posY2);
      if (posY > 7 || posX2 === 0) {
        isScroll = true;
        allowSwipe = false;
      } else if (posY < 7) {
        isSwipe = true;
      }
    }

    if (isSwipe) {
      if (slideIndex === 0) {
        if (posInit < posX1) {
          setTransform(transform, 0);
          return;
        } else {
          allowSwipe = true;
        }
      }

      // запрет ухода вправо на последнем слайде
      if (slideIndex === --slides.length) {
        if (posInit > posX1) {
          setTransform(transform, lastTrf);
          return;
        } else {
          allowSwipe = true;
        }
      }

      if (posInit > posX1 && transform < nextTrf || posInit < posX1 && transform > prevTrf) {
        reachEdge();
        return;
      }

      sliderTrack.style.transform = `translate3d(${transform - posX2}px, 0px, 0px)`;
    }

  },
  swipeEnd = function() {
    posFinal = posInit - posX1;

    isScroll = false;
    isSwipe = false;

    document.removeEventListener('touchmove', swipeAction);
    document.removeEventListener('mousemove', swipeAction);
    document.removeEventListener('touchend', swipeEnd);
    document.removeEventListener('mouseup', swipeEnd);

    sliderList.classList.add('grab');
    sliderList.classList.remove('grabbing');

    if (allowSwipe) {
      swipeEndTime = Date.now();
      if (Math.abs(posFinal) > posThreshold || swipeEndTime - swipeStartTime < 300) {
        if (posInit < posX1) {
          slideIndex--;
        } else if (posInit > posX1) {
          slideIndex++;
        }
      }

      if (posInit !== posX1) {
        allowSwipe = false;
        slide();
      } else {
        allowSwipe = true;
      }

    } else {
      allowSwipe = true;
    }

  },
  setTransform = function(transform, comapreTransform) {
    if (transform >= comapreTransform) {
      if (transform > comapreTransform) {
        sliderTrack.style.transform = `translate3d(${comapreTransform}px, 0px, 0px)`;
      }
    }
    allowSwipe = false;
  },
  reachEdge = function() {
    transition = false;
    swipeEnd();
    allowSwipe = true;
  };

sliderTrack.style.transform = 'translate3d(0px, 0px, 0px)';
sliderList.classList.add('grab');

sliderTrack.addEventListener('transitionend', () => allowSwipe = true);
slider.addEventListener('touchstart', swipeStart);
slider.addEventListener('mousedown', swipeStart);

arrows.addEventListener('click', function() {
  let target = event.target;

  if (target.classList.contains('next')) {
    slideIndex++;
  } else if (target.classList.contains('prev')) {
    slideIndex--;
  } else {
    return;
  }

  slide();
});
* {
  box-sizing: border-box;
}
.slider {
  position: relative;
  width: 200px;
  height: 200px;
  margin: 50px auto 0;
  user-select: none;
  touch-action: pan-y;
}

.slider img {
  poiner-events: none;
}

.slider-list {
  width: 200px;
  height: 200px;
  overflow: hidden;
}

.slider-list.grab {
  cursor: grab;
}

.slider-list.grabbing{
  cursor: grabbing;
}

.slider-track {
  display: flex;
}

.slide {
  width: 200px;
  height: 200px;
  flex-shrink: 0;
  font-size: 50px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid #000;
}

.slider-arrows {
  margin-top: 15px;
  text-align: center;
}

.next,
.prev {
  background: none;
  border: none;
  margin: 0 10px;
  font-size: 30px;
  cursor: pointer;
}

.next.disabled,
.prev.disabled {
  opacity: .25;
  pointer-events: none;
}





.myImg {
  cursor: pointer;
  transition: 0.3s;
}
.myImg:hover {
  opacity: 0.7;
}
.modals {
  opacity: 0;
  visibility: hidden; 
  position: fixed;
  z-index: 1;
  padding-top: 100px;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  overflow: auto;
  background-color: rgb(0, 0, 0);
  background-color: rgba(0, 0, 0, 0.9);
}
.slide {
  margin: auto;
  display: block;
   pointer-events: none; 
  width: 80%;
  max-width: 700px;
}
.modal-content {
  -webkit-animation-name: zoom;
  -webkit-animation-duration: 0.6s;
  animation-name: zoom;
  animation-duraion: 0.6s;
}
/* Next & previous buttons */

.prev,
.next {
  cursor: pointer;
  position: absolute;
  top: 0;
  top: 50%;
  width: auto;
  margin-top: -22px;
  padding: 16px;
  color: white;
  font-weight: bold;
  font-size: 18px;
  transition: 0.6s ease;
  border-radius: 0 3px 3px 0;
}
/* Position the "next button" to the right */

.next {
  right: 0;
  border-radius: 3px 0 0 3px;
}
/* On hover, add a black background color with a little bit see-through */

.prev:hover,
.next:hover {
  background-color: rgba(0, 0, 0, 0.8);
}
@-webkit-keyframes zoom {
  from {
    -webkit-transform: scale(0)
  }
  to {
    -webkit-transform: scale(1)
  }
}
@keyframes zoom {
  from {
    transform: scale(0)
  }
  to {
    transform: scale(1)
  }
}
.close {
  position: absolute;
  top: 15px;
  right: 35px;
  color: #f1f1f1;
  font-size: 40px;
  font-weight: bold;
  transition: 0.3s;
}
.close:hover,
.close:focus {
  color: #bbb;
  text-decoration: none;
  cursor: pointer;
}
@media only screen and (max-width: 700px) {
  .modal-content {
    width: 100%;
  }
}
<img class="myImg" src="https://avatars.mds.yandex.net/i?id=33abbd5b9cd92f436a1809e42b3709f5_l-11951399-images-thumbs&n=13" width="300" height="150">
<img class="myImg" src="https://avatars.mds.yandex.net/i?id=e358e8e717dcf1c4a1e7a9523fb5e12d_l-5103756-images-thumbs&n=13" width="300" height="150">
<img class="myImg" src="https://avatars.mds.yandex.net/i?id=cd915280ba6e522fbfc319550795f627_l-5479024-images-thumbs&n=13" width="300" height="150">


<div id="myModal" class="modals">
    <span class="close">X</span>
    <div class="slider">
        <div class="slider-list">
            <div class="slider-track">
                <img class="slide" id="img01">
            </div>
        </div>
        <div class="slider-arrows">
            <button type="button" class="prev">&larr;</button>
            <button type="button" class="next">&rarr;</button>
        </div>
    </div>
</div>

What I tried to do:
In the button click event, I added a search for an image by data-attribute. It worked, but it switches images without animation. And I couldn’t do it for the swap event at all.

arrows.addEventListener('click', function() {
  let target = event.target;
  if (target.classList.contains('next')) {
    var count = parseInt(currentImage.getAttribute("data"));
    if (count > 0) currentImage = img[count - 1];
    modalImg.src = currentImage.src;
  } 
} 

TIFF (one page) file JS upload

At upload with <input type="file" multiple ... , the result is delivered in a Filelist that may be displayed in the browser console like, for example,

FileList(4) [ File, File, File, File ] ​
0: File { name: "screen1.png", lastModified: 1721751553347, size: 83438, … }
1: File { name: "screen0.png", lastModified: 1721751565931, size: 84859, … }
​2: File { name: "Vbm_G.tif", lastModified: 1756065117219, size: 11896266, … }
​3: File { name: "Vbm_C.tif", lastModified: 1755974450073, size: 2022734, … }
​length: 4

(and also, not displayed, type: "image/png"|"image/tiff" etc.)

Since it appears impossible to use URL.createObjectURL(FileBlob) with image/tiff, and nor, for example, ember-file-upload processes TIFFs, how is some way to handle tiff images by, at least, converting a TIFF FileBlob to, for example a PNG FileBlob? Which then may be utilized like any other type: "image/*" FileBlob (please don’t ask “why tiff”, that’s not my choice).


(FileBlob example: FileList[3])


Or, is there a way to save such a FileBlob to my application’s local web, in spite of the browser’s inability to show it? – Safari unavailable!

Electron app loading local React bundle – DOM never hydrates despite JS execution

I’m building a cross-platform application (web + desktop) where the React + Vite frontend is hosted on Vercel. While using mainWindow.loadURL('https://example-app.vercel.app') works, it has significant drawbacks:

  • No offline functionality
  • Poor performance due to lazy-loaded chunks being fetched from Vercel
  • Network dependency causes jankiness and slow loading

I can’t eliminate code splitting since it would make the web version unusable.

so heres what i did.

To solve this, I implemented a caching system:

  1. Show initial loading splash screen in Electron
  2. Fetch entire bundle from Vercel using manifest.json
  3. Store bundle in AppData with hash for cache validation
  4. Load main window with local bundle, unmount splash
  5. On subsequent launches, only fetch if local copy missing or hash mismatch

The Problem

The fetching and caching works perfectly – all chunks are downloaded and stored correctly. However, the DOM never hydrates:

  • index.html loads into the window successfully
  • All file paths in the HTML are correct and resolvable
  • JavaScript executes (debugger on first line of index.js triggers)
  • But div.App remains empty – the React app never mounts

Code Structure

// Simplified version of my approach
const loadLocalBundle = async () => {
  // Fetch and cache logic works fine
  await downloadAndCacheBundle();
  
  // Load the local index.html
  mainWindow.loadFile(path.join(bundlePath, 'index.html'));
};

What I’ve Verified

  • All chunks download successfully
  • File paths in index.html are correct
  • JavaScript files are executable (debugger triggers)
  • Local files are accessible via file:// protocol
  • React app never mounts/hydrates

Are there known issues with React/Vite bundles running from file:// protocol?
Could this be related to how Vite handles module resolution in local vs hosted environments?
What debugging steps can help identify why the React app isn’t initializing?

Any insights into why the DOM hydration fails despite successful JS execution would be greatly appreciated!

Unable to resolve imported file

The error: Unable to resolve ../components/ThemedView from app(auth)register.jsx

// ThemedView.jsx
import { View, useColorScheme } from 'react-native'
import { Colors } from '../constants/Colors'

const ThemedView = ({ style, ...props }) => {
    const colorScheme = useColorScheme()
    const theme = Colors[colorScheme] ?? Colors.light

  return (
    <View style={[{backgroundColor: theme.background,}, style]}
        {...props}
        />
  )
}

export default ThemedView

register.jsx

import { StyleSheet } from 'react-native';
import { Link } from 'expo-router';
import ThemedView from '../components/ThemedView';
import Spacer from '../components/Spacer';
import ThemedText from '../components/ThemedText';

const Register = () => {
  return (
    <ThemedView styles={styles.container}>

    <Spacer/>
      <ThemedText title = {true} style={styles.title}>
        Register for an Account
        </ThemedText>

     <Spacer height = {100}/>

     <Link href={'/login'} style={styles.link}>
      <ThemedText textAlign = {'center'}>
          Login Instead
      </ThemedText>
      </Link>
    </ThemedView>
  )
}

export default Register

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
  },
  title: {
    fontSize: 18,
    marginBottom: 30,
    textAlign: 'center',
  },

  link: {
    marginVertical: 10,
    borderBottomWidth: 1,
  },
});

My folder structure

I tried searching on here and online but couldn’t get anywhere, this is from an expo tutorial Net Ninja – Complete React Native Tutorial #7 – Route Groups & Nested Layouts

I want Orientation Bounding box in Fabric.js , Please help me out

I am currently working in a project which required me to have bounding box clipped to the polygon that is being used in that scenario. And when i press save and then load , it should not have Axis Aligned Bounding Box (AABB) , i want it to have OBB( Orientation bounding box) . I am using fabric-with-touch and it does not have controlsUtils as well. The version is though 5.3.1 . Any help would be appy

I tried creating every custom function, be it scaling equally or rotation with snapping, but it is too complex for me

40% of Firebase User Registrations Missing Firestore Documents

40% of Firebase User Registrations Missing Firestore Documents

I’m experiencing a critical issue with my NextJS/Firebase application where approximately 40% of random user registrations result in “orphaned” users – users that exist in Firebase Authentication but have no corresponding document in Firestore.

The issue occurs randomly with no consistent pattern or error messages:

  1. Users are 100% successfully created in Firebase Authentication
  2. But approximately 40% of random registration attempts fail to create the Firestore document
  3. No errors are thrown during this process
  4. Affected users see a “User not approved” error on our Login form
  5. These users can’t get their pending registrations approved from the admin side because our app checks for Firestore documents

Code

Here’s my user registration function in AuthContext.js:

// New user registration
const register = async (email, password, userData) => {
  setErrorMessage('');
  let user = null;
  
  try {
    setLoading(true);
    
    // 1. Create user in Firebase Auth
    const userCredential = await createUserWithEmailAndPassword(auth, email, password);
    user = userCredential.user;
    
    // 2. Create user document data
    const userDocData = {
      email: email,
      name: userData.name || '',
      surname: userData.surname || '',
      phone: userData.phone || '',
      role: userData.role || 'student',
      status: 'pending',
      createdAt: serverTimestamp(),
      updatedAt: serverTimestamp(),
      package: userData.role === 'student' ? {
        remainingClasses: 0,
        paymentStatus: false
      } : null
    };
    
    // 3. Create document in Firestore
    const batch = writeBatch(db);
    batch.set(doc(db, 'users', user.uid), userDocData);
    await batch.commit();
    
    // 4. Verify document creation
    const checkDoc = await getDoc(doc(db, 'users', user.uid));
    if (!checkDoc.exists()) {
      throw new Error('User document was not created');
    }
    
    // 5. Logout after successful registration
    await signOut(auth);
    
    return user;
    
  } catch (error) {
    // If Auth user created but Firestore document failed, delete Auth user
    if (user) {
      try {
        // Try to delete any partially created document
        try {
          await deleteDoc(doc(db, 'users', user.uid));
        } catch (docError) {
          console.error("Error deleting document:", docError);
        }
        
        // Delete Auth user
        await deleteUser(user);
      } catch (deleteError) {
        console.error('Error deleting user after failed registration:', deleteError);
      }
    }
    
    // Handle specific errors
    switch (error.code) {
      case 'auth/email-already-in-use':
        setErrorMessage('Αυτή η διεύθυνση email χρησιμοποιείται ήδη.');
        break;
      case 'auth/invalid-email':
        setErrorMessage('Μη έγκυρη διεύθυνση email.');
        break;
      case 'auth/weak-password':
        setErrorMessage('Ο κωδικός πρέπει να έχει τουλάχιστον 6 χαρακτήρες.');
        break;
      default:
        setErrorMessage('Σφάλμα κατά την εγγραφή. Παρακαλώ δοκιμάστε ξανά.');
        break;
    }
    throw error;
  } finally {
    setLoading(false);
  }
};

I’ve also tried implementing a retry mechanism with exponential backoff:

// 3. Create document with retry mechanism
let success = false;
let attempts = 0;

while (!success && attempts < 5) {
  try {
    attempts++;
    console.log(`Attempt ${attempts} to create Firestore document`);
    
    const batch = writeBatch(db);
    batch.set(doc(db, 'users', user.uid), userDocData);
    await batch.commit();
    
    // Immediate verification
    const checkDoc = await getDoc(doc(db, 'users', user.uid));
    if (checkDoc.exists()) {
      success = true;
      console.log(`Successfully created Firestore document on attempt ${attempts}`);
    } else {
      console.warn(`Document check failed after batch commit (attempt ${attempts})`);
      // Wait before retrying (exponential backoff)
      await new Promise(resolve => setTimeout(resolve, 1000 * attempts));
    }
  } catch (error) {
    console.error(`Firestore error on attempt ${attempts}:`, error);
    // Wait before retrying
    await new Promise(resolve => setTimeout(resolve, 1000 * attempts));
  }
}

if (!success) {
  throw new Error(`Failed to create Firestore document after ${attempts} attempts`);
}

I’ve also implemented a temporary workaround in onAuthStateChanged:

useEffect(() => {
  const unsubscribe = onAuthStateChanged(auth, async (user) => {
    if (user) {
      try {
        // Check if document exists
        const userDoc = await getDoc(doc(db, 'users', user.uid));
        
        // If document doesn't exist, create it (fix orphaned users)
        if (!userDoc.exists()) {
          console.log("Orphaned user detected. Synchronizing...");
          
          await setDoc(doc(db, 'users', user.uid), {
            email: user.email,
            name: '',
            surname: '',
            phone: '',
            role: 'student',
            status: 'pending',
            createdAt: serverTimestamp(),
            updatedAt: serverTimestamp(),
            package: {
              remainingClasses: 0,
              paymentStatus: false
            }
          });
          
          // Continue with normal checks...
        }
      } catch (error) {
        console.error('Error fetching user data:', error);
        setCurrentUser(null);
      }
    } else {
      setCurrentUser(null);
    }
    setLoading(false);
  });

  return () => unsubscribe();
}, [auth, db, router]);

What I’ve Tried

  1. Using writeBatch instead of setDoc – same issue occurs
  2. Adding a retry mechanism with up to 5 attempts – still fails occasionally
  3. Adding verification after document creation – confirms document isn’t being created
  4. Adding proper cleanup to delete orphaned Authentication users
  5. Adding detailed logging – no errors appear in logs when the issue occurs

Environment

  • NextJS 14
  • Firebase v9
  • Using the client SDK
  • Using the initialization pattern with singleton instance

Question

What could be causing this inconsistent behavior where Firebase Auth users are created but Firestore documents fail silently ~40% of the time? I need a reliable solution as this affects a significant portion of our user registrations.

Is there a way to ensure atomicity between Auth and Firestore operations? Would using Firebase Cloud Functions for user creation provide better reliability?

How to design an online , low-cost face-recognition attendance system for rural schools? [closed]

We’re building an attendance web-app for rural schools (mobile-first). Requirements: client-side face recognition, local storage syncs when online, works on low-end phones/laptops monolingual( English) UI, CSV export, privacy (face embeddings stored locally). What libraries/approach do you recommend for reliable offline face recognition and local storage (IndexedDB/SQLite/PWA)?Thanks!
Important:
my team (6 members) has no programming or web-tech experience — none of us know HTML, CSS, or JavaScript. We need beginner-friendly, low-effort options.

We are complete beginners with no prior programming or web development experience. This is our very first project, and we are building it as part of Smart India Hackathon 2025. We are still learning and exploring the right tools and approaches.