``` ├── .github/ ├── ISSUE_TEMPLATE/ ├── bug_report.md ├── feature_request.md ├── dependabot.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── app/ ├── [locale]/ ├── [...rest]/ ├── page.tsx ├── layout.tsx ├── page.tsx ├── template/ ├── [id]/ ├── page.tsx ├── api/ ├── invoice/ ├── export/ ├── route.ts ├── generate/ ├── route.ts ├── send/ ├── route.ts ├── components/ ├── dev/ ├── DevDebug.tsx ├── index.ts ├── invoice/ ├── InvoiceActions.tsx ├── InvoiceForm.tsx ├── InvoiceMain.tsx ├── actions/ ├── FinalPdf.tsx ├── LivePreview.tsx ├── PdfViewer.tsx ├── form/ ├── Charges.tsx ├── SingleItem.tsx ├── TemplateSelector.tsx ├── sections/ ├── BillFromSection.tsx ├── BillToSection.tsx ├── ImportJsonButton.tsx ├── InvoiceDetails.tsx ├── InvoiceSummary.tsx ├── Items.tsx ├── PaymentInformation.tsx ├── wizard/ ├── WizardNavigation.tsx ├── WizardProgress.tsx ├── WizardStep.tsx ├── layout/ ├── BaseFooter.tsx ├── BaseNavbar.tsx ├── modals/ ├── alerts/ ├── NewInvoiceAlert.tsx ├── email/ ├── SendPdfToEmailModal.tsx ├── invoice/ ├── InvoiceExportModal.tsx ├── InvoiceLoaderModal.tsx ├── components/ ├── SavedInvoicesList.tsx ├── signature/ ├── SignatureModal.tsx ├── components/ ├── SignatureColorSelector.tsx ├── SignatureFontSelector.tsx ├── tabs/ ├── DrawSignature.tsx ├── TypeSignature.tsx ├── UploadSignature.tsx ├── reusables/ ├── BaseButton.tsx ├── LanguageSelector.tsx ├── Subheading.tsx ├── ThemeSwitcher.tsx ├── form-fields/ ├── ChargeInput.tsx ├── CurrencySelector.tsx ├── DatePickerFormField.tsx ├── FormCustomInput.tsx ├── FormFile.tsx ├── FormInput.tsx ├── FormTextarea.tsx ├── templates/ ├── email/ ├── SendPdfEmail.tsx ├── invoice-pdf/ ├── DynamicInvoiceTemplate.tsx ├── InvoiceLayout.tsx ├── InvoiceTemplate1.tsx ├── InvoiceTemplate2.tsx ├── globals.css ├── layout.tsx ├── not-found.tsx ├── page.tsx ├── robots.txt ├── components.json ├── components/ ├── ui/ ├── alert-dialog.tsx ├── aspect-ratio.tsx ├── badge.tsx ├── button.tsx ├── calendar.tsx ├── card.tsx ├── dialog.tsx ├── form.tsx ├── input.tsx ├── label.tsx ├── navigation-menu.tsx ├── popover.tsx ├── scroll-area.tsx ├── select.tsx ├── skeleton.tsx ├── switch.tsx ├── table.tsx ├── tabs.tsx ├── textarea.tsx ├── toast.tsx ├── toaster.tsx ├── tooltip.tsx ├── use-toast.ts ├── contexts/ ├── ChargesContext.tsx ├── InvoiceContext.tsx ├── Providers.tsx ├── SignatureContext.tsx ``` ## /.github/ISSUE_TEMPLATE/bug_report.md --- name: Bug Report about: Create a bug report to help us improve title: "[BUG] - " --- #### Issue Summary [Concise description of the issue] #### Environment - **Browser/Platform:** [Insert Browser/Platform Name and Version] #### Steps to Reproduce 1. [First step to reproduce the issue] 2. [Second step to reproduce the issue] 3. [And so on...] #### Expected Behavior [What you expected to happen] #### Actual Behavior [What actually happened] #### Screenshots/Logs [If applicable, include relevant screenshots or error logs] #### Additional Information [Include any additional information that may help in diagnosing the issue] #### Possible Solutions [If you have suggestions for how to fix the issue, provide them here] --- #### Acceptance Criteria - [ ] The issue is confirmed and reproducible. - [ ] Relevant details about the environment are provided. - [ ] Steps to reproduce are clear and concise. - [ ] Expected and actual behavior are described. - [ ] Screenshots/logs (if applicable) are attached. - [ ] Any additional relevant information is included. --- [Note: Please replace the placeholders in square brackets with specific information related to your bug report.] ## /.github/ISSUE_TEMPLATE/feature_request.md --- name: Feature Request about: Suggest an enhancement or a new feature title: "[FEATURE] - " --- #### Feature Summary [Concise description of the feature or enhancement] #### Use Case [Explain the specific use case or scenario where this feature would be beneficial] #### Proposed Solution [Describe your proposed solution or how you envision implementing this feature] #### Additional Information [Include any additional context or details that support your feature request] --- [Note: Please replace the placeholders in square brackets with specific information related to your feature request.] ## /.github/dependabot.yml ```yml path="/.github/dependabot.yml" # To get started with Dependabot version updates, you'll need to specify which # package ecosystems to update and where the package manifests are located. # Please see the documentation for all configuration options: # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates version: 2 updates: - package-ecosystem: "npm" directory: "/" schedule: interval: "daily" open-pull-requests-limit: 10 versioning-strategy: "lockfile-only" allow: - dependency-type: "all" ignore: - dependency-name: "next" - dependency-name: "react" - dependency-name: "react-dom" - dependency-name: "@types/node" - dependency-name: "@types/react" - dependency-name: "@types/react-dom" ``` ## /.gitignore ```gitignore path="/.gitignore" # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies /node_modules /.pnp .pnp.js # testing /coverage # next.js /.next/ /out/ # production /build # misc .DS_Store *.pem # debug npm-debug.log* yarn-debug.log* yarn-error.log* # local env files .env*.local # vercel .vercel # typescript *.tsbuildinfo next-env.d.ts # cache /.cache ``` ## /Dockerfile ``` path="/Dockerfile" FROM node:22-alpine AS build WORKDIR /app COPY package* . RUN npm install COPY . . RUN npm run build FROM node:22-alpine AS production RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs COPY --from=build --chown=nextjs:nodejs /app/.next ./.next COPY --from=build --chown=nextjs:nodejs /app/node_modules ./node_modules COPY --from=build --chown=nextjs:nodejs /app/package.json ./package.json COPY --from=build --chown=nextjs:nodejs /app/public ./public EXPOSE 3000 CMD npm start ``` ## /LICENSE ``` path="/LICENSE" MIT License Copyright (c) 2023 Ali Abbasov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ``` ## /README.md [![Discord](https://img.shields.io/badge/Discord-%40Invoify-000000?style=flat&logo=Discord&logoColor=#5865F2)](https://discord.gg/uhXKHbVKHZ) # Invoify Invoify is a web-based invoice generator application built with Next.js 13, TypeScript, React, and the Shadcn UI library. It provides an easy way to create and manage professional invoices. ![Invoify Website image](/public/assets/img/invoify-web-app.png) ## Table of Contents - [Invoify](#invoify) - [Table of Contents](#table-of-contents) - [Technologies](#technologies) - [Core Technologies](#core-technologies) - [Additional Dependencies](#additional-dependencies) - [Roadmap](#roadmap) - [Demo](#demo) - [Getting Started](#getting-started) - [Prerequisites](#prerequisites) - [Installation](#installation) - [License](#license) ## Technologies ### Core Technologies - **Next.js:** React framework for SSR and client-side navigation. - **TypeScript:** JavaScript superset with static typing. - **Shadcn-UI:** UI library for enhanced visuals. - **Tailwind:** Utility-first CSS framework. - **React Hook Form:** Form management for React. - **Zod:** TypeScript-first schema validation. - **Puppeteer:** PDF generation with headless browsers. ### Additional Dependencies - **Nodemailer:** Node.js module for sending emails. - **Lucide Icons:** Collection of customizable SVG icons. ## Roadmap - [x] **Easily Create Invoices:** Utilize a simple form to quickly generate invoices. - [x] **Save for Future Access:** Store your invoices directly in your browser for easy retrieval. - [x] **Retrieve Invoices Effortlessly:** Load and access invoices seamlessly from your saved list. - [x] **Flexible Download Options:** Download invoices directly or send them via email in PDF format. - [x] **Template Variety:** Choose from multiple (currently 2) invoice templates. - [x] **Live Preview:** Edit the form and see changes in real-time with the live preview feature. - [x] **Export in Various Formats:** Export invoices in different formats, including JSON, XLSX, CSV, and XML. - [ ] **I18N Support:** i18n support with multiple languages for UI and templates. - [ ] **Themeable Templates:** Select a theme color for the invoice - [ ] **Custom Inputs:** Define your own inputs that are missing from the default invoice builder. (Ex: VAT number) - [ ] **Individual Tax for Line Items:** Add tax details for a specific line item other than the general tax ## Demo > [!NOTE] > Please be advised that there are currently issues when using this application in the Mozilla Firefox browser. For more information, refer to [Issue #11](https://github.com/aliabb01/invoify/issues/11). Visit the [live demo](https://invoify.vercel.app) to see Invoify in action. ## Getting Started Follow these instructions to get Invoify up and running on your local machine. ### Prerequisites - Node.js and npm installed on your system. ### Installation 1. Clone the repository: ```bash git clone https://github.com/al1abb/invoify.git cd invoify ``` 2. Install dependencies ```bash npm install ``` 3. Create an .env.local file with this content (This step is for sending pdf to email feature): ```env NODEMAILER_EMAIL=your_email@example.com NODEMAILER_PW=your_email_password ``` 4. Start development server ```bash npm run dev ``` 5. Open your web browser and access the application at [http://localhost:3000](http://localhost:3000) ## License Distributed under the MIT License. See `LICENSE.txt` for more information. ## Discord Join the Discord server [here](https://discord.gg/uhXKHbVKHZ) ## /app/[locale]/[...rest]/page.tsx ```tsx path="/app/[locale]/[...rest]/page.tsx" import { notFound } from "next/navigation"; export default function CatchAllPage() { notFound(); } ``` ## /app/[locale]/layout.tsx ```tsx path="/app/[locale]/layout.tsx" import type { Metadata } from "next"; import { notFound } from "next/navigation"; // Fonts import { alexBrush, dancingScript, greatVibes, outfit, parisienne, } from "@/lib/fonts"; // Favicon import Favicon from "@/public/assets/favicon/favicon.ico"; // Vercel Analytics import { Analytics } from "@vercel/analytics/react"; // Next Intl import { NextIntlClientProvider } from "next-intl"; // ShadCn import { Toaster } from "@/components/ui/toaster"; // Components import { BaseNavbar, BaseFooter } from "@/app/components"; // Contexts import Providers from "@/contexts/Providers"; // SEO import { JSONLD, ROOTKEYWORDS } from "@/lib/seo"; // Variables import { BASE_URL, GOOGLE_SC_VERIFICATION, LOCALES } from "@/lib/variables"; export const metadata: Metadata = { title: "Invoify | Free Invoice Generator", description: "Create invoices effortlessly with Invoify, the free invoice generator. Try it now!", icons: [{ rel: "icon", url: Favicon.src }], keywords: ROOTKEYWORDS, viewport: "width=device-width, initial-scale=1", robots: { index: true, follow: true, }, alternates: { canonical: BASE_URL, }, authors: { name: "Ali Abbasov", url: "https://aliabb.vercel.app", }, verification: { google: GOOGLE_SC_VERIFICATION, }, }; export function generateStaticParams() { const locales = LOCALES.map((locale) => locale.code); return locales; } export default async function LocaleLayout({ children, params: { locale }, }: { children: React.ReactNode; params: { locale: string }; }) { let messages; try { messages = (await import(`@/i18n/locales/${locale}.json`)).default; } catch (error) { notFound(); } return (