DEV Community

Roberto Luna
Roberto Luna

Posted on

Implementing an Elegant Confirm Dialog and Product Export in Next.js

Implementing an Elegant Confirm Dialog and Product Export in Next.js

TL;DR: I replaced window.confirm with a custom ConfirmDialog component and implemented a product export feature using Next.js API routes. These changes improve the user experience and provide a more secure way to handle confirmations and data exports.

The Problem

The initial problem was to replace the native window.confirm function with a more customizable and elegant solution. Additionally, there was a need to export products data in CSV format. The existing implementation was using window.confirm which was not providing a good user experience.

What I Tried First

Initially, I tried to use a simple alert function to handle confirmations, but it was not providing the desired user experience. I also attempted to use a third-party library for handling confirmations, but it was not customizable enough.

The Implementation

Confirm Dialog Component

I created a new component called ConfirmDialog which uses React state to manage the confirmation process.

// components/ui/confirm-dialog.tsx
"use client";
import { useState } from "react";
import { AlertTriangle, Loader2 } from "lucide-react";
import { Button } from "@/components/ui/button";

interface ConfirmDialogProps {
  title: string;
  description: string;
  onConfirm: () => void;
  onCancel: () => void;
}

const ConfirmDialog: React.FC<ConfirmDialogProps> = ({
  title,
  description,
  onConfirm,
  onCancel,
}) => {
  const [isLoading, setIsLoading] = useState(false);

  const handleConfirm = async () => {
    setIsLoading(true);
    await onConfirm();
    setIsLoading(false);
  };

  return (
    <div>
      <h2>{title}</h2>
      <p>{description}</p>
      <Button onClick={handleConfirm} disabled={isLoading}>
        {isLoading ? <Loader2 /> : "Confirm"}
      </Button>
      <Button onClick={onCancel} variant="outline">
        Cancel
      </Button>
    </div>
  );
};

export default ConfirmDialog;
Enter fullscreen mode Exit fullscreen mode

Product Export API Route

I created a new API route to handle product exports.

// app/api/export/products/route.ts
import { NextRequest, NextResponse } from "next/server";
import { auth } from "@/lib/auth";
import { prisma } from "@/lib/prisma";

export async function GET(req: NextRequest) {
  const products = await prisma.product.findMany();

  const csv = products
    .map((product) => Object.values(product).join(","))
    .join("\n");

  return new NextResponse(csv, {
    headers: {
      "Content-Type": "text/csv",
      "Content-Disposition": `attachment; filename="products.csv"`,
    },
  });
}
Enter fullscreen mode Exit fullscreen mode

Integration with Existing Components

I integrated the ConfirmDialog component with existing components that were using window.confirm.

// features/manuals/manual-card-actions.tsx
"use client";
import { ConfirmDialog, useConfirm } from "@/components/ui/confirm-dialog";
import { useState } from "react";
import { Download, BookOpen, Trash2 } from "lucide-react";

const ManualCardActions = () => {
  const { confirm } = useConfirm();

  const handleDelete = async () => {
    if (await confirm("Are you sure you want to delete this manual?")) {
      // Delete manual logic
    }
  };

  return (
    <div>
      <Button onClick={handleDelete}>
        <Trash2 />
      </Button>
      <ConfirmDialog
        title="Confirm Deletion"
        description="Are you sure you want to delete this manual?"
        onConfirm={handleDelete}
        onCancel={() => {}}
      />
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

Key Takeaway

The key takeaway from this implementation is to use custom components to handle confirmations and data exports, rather than relying on native browser functions or third-party libraries. This approach provides more control over the user experience and security.

What's Next

The next step is to implement more features to the ConfirmDialog component, such as support for custom icons and loading states. Additionally, I plan to implement more API routes for exporting other types of data.

vibecoding #buildinpublic #nextjs #react #typescript


Part of my Build in Public series — sharing the real process of building Building Ismerely KB from Playa del Carmen, México.

Repo: zaerohell/bienestar-integral-kb · 2026-07-03

#playadev #buildinpublic

Top comments (0)