├── .nvmrc ├── .prettierignore ├── .commitlintrc.json ├── pnpm-workspace.yaml ├── apps ├── www │ ├── public │ │ ├── og.jpg │ │ ├── og2.jpg │ │ ├── favicon.ico │ │ ├── hero-dark.png │ │ ├── hero-dark2.png │ │ ├── hero-light.png │ │ ├── hero-light2.png │ │ ├── images │ │ │ ├── hero.png │ │ │ ├── avatars │ │ │ │ └── shadcn.png │ │ │ └── blog │ │ │ │ ├── blog-post-1.jpg │ │ │ │ ├── blog-post-2.jpg │ │ │ │ ├── blog-post-3.jpg │ │ │ │ └── blog-post-4.jpg │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── apple-touch-icon.png │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── site.webmanifest │ │ └── vercel.svg │ ├── postcss.config.cjs │ ├── src │ │ ├── assets │ │ │ └── fonts │ │ │ │ ├── Inter-Bold.ttf │ │ │ │ ├── Inter-Regular.ttf │ │ │ │ ├── CalSans-SemiBold.ttf │ │ │ │ ├── CalSans-SemiBold.woff2 │ │ │ │ └── index.ts │ │ ├── content │ │ │ ├── authors │ │ │ │ ├── shadcn.mdx │ │ │ │ └── codehagen.mdx │ │ │ └── docs │ │ │ │ ├── in-progress.mdx │ │ │ │ ├── index.mdx │ │ │ │ └── documentation │ │ │ │ ├── index.mdx │ │ │ │ └── code-blocks.mdx │ │ ├── lib │ │ │ ├── email.ts │ │ │ ├── validations │ │ │ │ ├── auth.ts │ │ │ │ ├── user.ts │ │ │ │ └── og.ts │ │ │ ├── exceptions.ts │ │ │ ├── stripe.ts │ │ │ ├── crypto.ts │ │ │ ├── session.ts │ │ │ ├── pixel-events.tsx │ │ │ └── db.ts │ │ ├── components │ │ │ ├── analytics.tsx │ │ │ ├── users │ │ │ │ ├── UserCard.tsx │ │ │ │ ├── EmailButton.tsx │ │ │ │ ├── UserPowerCard.tsx │ │ │ │ ├── UserEmailCard.tsx │ │ │ │ ├── UsersCard.tsx │ │ │ │ └── UsersDashboardTable.tsx │ │ │ ├── buttons │ │ │ │ ├── DocsButton.tsx │ │ │ │ ├── StartOnbordaButton.tsx │ │ │ │ ├── LanguageButton.tsx │ │ │ │ ├── GenerateDescriptionsButton.tsx │ │ │ │ ├── SubmitProperty.tsx │ │ │ │ └── GetStartedButton.tsx │ │ │ ├── dashboard │ │ │ │ ├── shell.tsx │ │ │ │ ├── header.tsx │ │ │ │ ├── generatedtext.tsx │ │ │ │ ├── calltoaction.tsx │ │ │ │ └── beam-section.tsx │ │ │ ├── UserBadge.tsx │ │ │ ├── modal-provider.tsx │ │ │ ├── shared │ │ │ │ ├── card-skeleton.tsx │ │ │ │ ├── user-avatar.tsx │ │ │ │ ├── callout.tsx │ │ │ │ └── modal.tsx │ │ │ ├── properties │ │ │ │ ├── NoSummaryPlaceholder.tsx │ │ │ │ ├── NoPhotoPlaceholder copy.tsx │ │ │ │ ├── NoTextPlaceholder.tsx │ │ │ │ ├── PropertyPicture.tsx │ │ │ │ └── PropertyImageWithOptions.tsx │ │ │ ├── tailwind-indicator.tsx │ │ │ ├── docs │ │ │ │ ├── page-header.tsx │ │ │ │ └── search.tsx │ │ │ ├── open-page │ │ │ │ ├── OpenStartupSection.tsx │ │ │ │ ├── OpenUsersText.tsx │ │ │ │ └── OpenMiddleSection.tsx │ │ │ ├── ui │ │ │ │ ├── sphere-mask.tsx │ │ │ │ ├── text-shimmer.tsx │ │ │ │ ├── feature-card.tsx │ │ │ │ ├── marquee.tsx │ │ │ │ ├── confetti.tsx │ │ │ │ ├── border-beam.tsx │ │ │ │ └── animated-list.tsx │ │ │ ├── content │ │ │ │ └── mdx-card.tsx │ │ │ ├── providers.tsx │ │ │ ├── landing │ │ │ │ └── bottom-section-landing.tsx │ │ │ ├── forms │ │ │ │ └── billing-form-button.tsx │ │ │ ├── layout │ │ │ │ ├── mode-toggle.tsx │ │ │ │ └── mobile-nav.tsx │ │ │ ├── billing-info.tsx │ │ │ └── table │ │ │ │ └── dashboard │ │ │ │ └── propertystatus.tsx │ │ ├── app │ │ │ ├── api │ │ │ │ ├── auth │ │ │ │ │ └── [...nextauth] │ │ │ │ │ │ └── route.ts │ │ │ │ └── ai │ │ │ │ │ └── generateDescription │ │ │ │ │ └── route.ts │ │ │ ├── (auth) │ │ │ │ └── layout.tsx │ │ │ ├── robots.ts │ │ │ ├── (docs) │ │ │ │ ├── guides │ │ │ │ │ └── layout.tsx │ │ │ │ ├── docs │ │ │ │ │ └── layout.tsx │ │ │ │ └── layout.tsx │ │ │ ├── (property) │ │ │ │ └── property │ │ │ │ │ └── [id] │ │ │ │ │ ├── report │ │ │ │ │ └── page.tsx │ │ │ │ │ ├── settings │ │ │ │ │ ├── loading.tsx │ │ │ │ │ └── page.tsx │ │ │ │ │ ├── loading.tsx │ │ │ │ │ ├── pictures │ │ │ │ │ └── loading.tsx │ │ │ │ │ └── layout.tsx │ │ │ ├── (marketing) │ │ │ │ ├── error.tsx │ │ │ │ ├── layout.tsx │ │ │ │ ├── blog │ │ │ │ │ └── page.tsx │ │ │ │ ├── pricing │ │ │ │ │ ├── page.tsx │ │ │ │ │ └── loading.tsx │ │ │ │ └── open │ │ │ │ │ ├── loading.tsx │ │ │ │ │ └── page.tsx │ │ │ ├── (dashboard) │ │ │ │ └── dashboard │ │ │ │ │ ├── billing │ │ │ │ │ └── loading.tsx │ │ │ │ │ ├── settings │ │ │ │ │ └── loading.tsx │ │ │ │ │ ├── notifications │ │ │ │ │ ├── loading.tsx │ │ │ │ │ └── page.tsx │ │ │ │ │ ├── loading.tsx │ │ │ │ │ ├── users │ │ │ │ │ └── page.tsx │ │ │ │ │ ├── [id] │ │ │ │ │ └── page.tsx │ │ │ │ │ └── layout.tsx │ │ │ ├── sitemap.ts │ │ │ └── (onboarding) │ │ │ │ └── onboarding │ │ │ │ └── layout.tsx │ │ ├── hooks │ │ │ ├── use-mounted.ts │ │ │ ├── use-signin-modal.ts │ │ │ ├── use-language-modal.ts │ │ │ ├── use-lock-body.ts │ │ │ ├── use-scroll.ts │ │ │ ├── use-local-storage.ts │ │ │ ├── use-intersection-observer.ts │ │ │ └── use-media-query.ts │ │ ├── types │ │ │ ├── next-auth.d.ts │ │ │ ├── types.ts │ │ │ └── index.d.ts │ │ ├── actions │ │ │ ├── get-metrics-user.ts │ │ │ ├── get-notification-settings.ts │ │ │ ├── delete-customer.ts │ │ │ ├── get-user-details.ts │ │ │ ├── change-customer-details.ts │ │ │ ├── delete-event.ts │ │ │ ├── get-channel-details.ts │ │ │ ├── update-property-description.tsx │ │ │ ├── get-customers-all.ts │ │ │ ├── update-language.tsx │ │ │ ├── update-property-form.tsx │ │ │ ├── get-channels.ts │ │ │ ├── select-option.ts │ │ │ ├── update-user-name.ts │ │ │ ├── update-property-status.ts │ │ │ ├── get-credits.ts │ │ │ ├── send-onboarding-email.ts │ │ │ ├── create-channel.ts │ │ │ ├── upload-images.ts │ │ │ ├── generate-api-key.ts │ │ │ ├── testwebhook.ts │ │ │ └── stats │ │ │ │ └── get-users-stats.ts │ │ ├── config │ │ │ ├── site.ts │ │ │ ├── marketing.ts │ │ │ ├── property.ts │ │ │ └── dashboard.ts │ │ ├── get-user-channels.ts │ │ ├── styles │ │ │ └── mdx.css │ │ ├── middleware.ts │ │ └── emails │ │ │ └── components │ │ │ └── footer.tsx │ ├── tsconfig.json │ ├── next.config.js │ └── eslint.config.js └── api │ ├── README.md │ ├── .gitignore │ ├── src │ ├── lib │ │ ├── generateApiKey.ts │ │ ├── parsePrismaError.ts │ │ └── db.ts │ ├── env.ts │ ├── validators │ │ └── types.ts │ ├── routes │ │ └── apiKeyMiddleware.ts │ ├── notifications │ │ └── discord │ │ │ └── sendDiscordNotification.ts │ └── index.ts │ ├── tsconfig.json │ ├── wrangler.toml │ └── package.json ├── .husky ├── commit-msg └── pre-commit ├── packages ├── db │ ├── index.ts │ ├── eslint.config.js │ ├── tsconfig.json │ └── package.json └── ui │ ├── src │ ├── components │ │ ├── aspect-ratio.tsx │ │ ├── skeleton.tsx │ │ ├── collapsible.tsx │ │ ├── stepper │ │ │ ├── use-media-query.tsx │ │ │ └── use-stepper.tsx │ │ ├── textarea.tsx │ │ ├── label.tsx │ │ ├── input.tsx │ │ ├── separator.tsx │ │ ├── progress.tsx │ │ ├── toaster.tsx │ │ ├── hover-card.tsx │ │ ├── sonner.tsx │ │ ├── checkbox.tsx │ │ ├── tooltip.tsx │ │ ├── slider.tsx │ │ ├── popover.tsx │ │ ├── switch.tsx │ │ ├── badge.tsx │ │ ├── radio-group.tsx │ │ ├── avatar.tsx │ │ ├── toggle.tsx │ │ └── scroll-area.tsx │ └── utils.ts │ ├── tsconfig.json │ ├── eslint.config.js │ └── tailwind.config.ts ├── tooling ├── typescript │ ├── package.json │ ├── internal-package.json │ └── base.json ├── tailwind │ ├── tailwind.d.ts │ ├── tsconfig.json │ ├── eslint.config.js │ └── package.json ├── eslint │ ├── tsconfig.json │ ├── nextjs.js │ ├── react.js │ ├── package.json │ ├── base.js │ └── types.d.ts └── prettier │ ├── tsconfig.json │ ├── package.json │ └── index.js ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── feature_request.yml │ └── bug_report.yml └── workflows │ └── take-issue.yml ├── .gitignore ├── .npmrc ├── turbo.json ├── package.json └── .env.example /.nvmrc: -------------------------------------------------------------------------------- 1 | v20.10.0 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | .next 4 | build 5 | .contentlayer -------------------------------------------------------------------------------- /.commitlintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@commitlint/config-conventional"] 3 | } 4 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - apps/* 3 | - packages/* 4 | - tooling/* 5 | -------------------------------------------------------------------------------- /apps/www/public/og.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codehagen/Dingify/HEAD/apps/www/public/og.jpg -------------------------------------------------------------------------------- /apps/www/public/og2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codehagen/Dingify/HEAD/apps/www/public/og2.jpg -------------------------------------------------------------------------------- /apps/api/README.md: -------------------------------------------------------------------------------- 1 | ``` 2 | npm install 3 | npm run dev 4 | ``` 5 | 6 | ``` 7 | npm run deploy 8 | ``` 9 | -------------------------------------------------------------------------------- /apps/www/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /apps/www/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codehagen/Dingify/HEAD/apps/www/public/favicon.ico -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx commitlint --edit $1 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx pretty-quick --staged 5 | -------------------------------------------------------------------------------- /apps/www/public/hero-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codehagen/Dingify/HEAD/apps/www/public/hero-dark.png -------------------------------------------------------------------------------- /apps/www/public/hero-dark2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codehagen/Dingify/HEAD/apps/www/public/hero-dark2.png -------------------------------------------------------------------------------- /apps/www/public/hero-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codehagen/Dingify/HEAD/apps/www/public/hero-light.png -------------------------------------------------------------------------------- /apps/www/public/hero-light2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codehagen/Dingify/HEAD/apps/www/public/hero-light2.png -------------------------------------------------------------------------------- /apps/www/public/images/hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codehagen/Dingify/HEAD/apps/www/public/images/hero.png -------------------------------------------------------------------------------- /apps/www/public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codehagen/Dingify/HEAD/apps/www/public/favicon-16x16.png -------------------------------------------------------------------------------- /apps/www/public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codehagen/Dingify/HEAD/apps/www/public/favicon-32x32.png -------------------------------------------------------------------------------- /apps/www/public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codehagen/Dingify/HEAD/apps/www/public/apple-touch-icon.png -------------------------------------------------------------------------------- /apps/www/src/assets/fonts/Inter-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codehagen/Dingify/HEAD/apps/www/src/assets/fonts/Inter-Bold.ttf -------------------------------------------------------------------------------- /apps/www/src/content/authors/shadcn.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: shadcn 3 | avatar: /images/avatars/shadcn.png 4 | twitter: shadcn 5 | --- 6 | -------------------------------------------------------------------------------- /apps/www/public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codehagen/Dingify/HEAD/apps/www/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /apps/www/public/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codehagen/Dingify/HEAD/apps/www/public/android-chrome-512x512.png -------------------------------------------------------------------------------- /apps/www/public/images/avatars/shadcn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codehagen/Dingify/HEAD/apps/www/public/images/avatars/shadcn.png -------------------------------------------------------------------------------- /apps/www/public/images/blog/blog-post-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codehagen/Dingify/HEAD/apps/www/public/images/blog/blog-post-1.jpg -------------------------------------------------------------------------------- /apps/www/public/images/blog/blog-post-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codehagen/Dingify/HEAD/apps/www/public/images/blog/blog-post-2.jpg -------------------------------------------------------------------------------- /apps/www/public/images/blog/blog-post-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codehagen/Dingify/HEAD/apps/www/public/images/blog/blog-post-3.jpg -------------------------------------------------------------------------------- /apps/www/public/images/blog/blog-post-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codehagen/Dingify/HEAD/apps/www/public/images/blog/blog-post-4.jpg -------------------------------------------------------------------------------- /apps/www/src/assets/fonts/Inter-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codehagen/Dingify/HEAD/apps/www/src/assets/fonts/Inter-Regular.ttf -------------------------------------------------------------------------------- /apps/www/src/content/authors/codehagen.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Christer 3 | avatar: /images/avatars/shadcn.png 4 | twitter: codehagen 5 | --- 6 | -------------------------------------------------------------------------------- /apps/www/src/assets/fonts/CalSans-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codehagen/Dingify/HEAD/apps/www/src/assets/fonts/CalSans-SemiBold.ttf -------------------------------------------------------------------------------- /packages/db/index.ts: -------------------------------------------------------------------------------- 1 | export * from "@prisma/client"; 2 | export * from "@prisma/adapter-neon"; 3 | export * from "@neondatabase/serverless"; 4 | -------------------------------------------------------------------------------- /apps/www/src/assets/fonts/CalSans-SemiBold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codehagen/Dingify/HEAD/apps/www/src/assets/fonts/CalSans-SemiBold.woff2 -------------------------------------------------------------------------------- /apps/www/src/lib/email.ts: -------------------------------------------------------------------------------- 1 | import { env } from "@/env"; 2 | import { Resend } from "resend"; 3 | 4 | export const resend = new Resend(env.RESEND_API_KEY); 5 | -------------------------------------------------------------------------------- /apps/www/src/lib/validations/auth.ts: -------------------------------------------------------------------------------- 1 | import * as z from "zod"; 2 | 3 | export const userAuthSchema = z.object({ 4 | email: z.string().email(), 5 | }); 6 | -------------------------------------------------------------------------------- /apps/www/src/lib/validations/user.ts: -------------------------------------------------------------------------------- 1 | import * as z from "zod"; 2 | 3 | export const userNameSchema = z.object({ 4 | name: z.string().min(3).max(32), 5 | }); 6 | -------------------------------------------------------------------------------- /tooling/typescript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dingify/tsconfig", 3 | "private": true, 4 | "version": "0.1.0", 5 | "files": [ 6 | "*.json" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /apps/www/src/lib/exceptions.ts: -------------------------------------------------------------------------------- 1 | export class RequiresProPlanError extends Error { 2 | constructor(message = "This action requires a pro plan") { 3 | super(message); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | contact_links: 3 | - name: ❓ Ask a question 4 | url: https://discord.gg/8BkX9hJRXs 5 | about: Ask questions about ProjectX 6 | -------------------------------------------------------------------------------- /apps/www/src/components/analytics.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Analytics as VercelAnalytics } from "@vercel/analytics/react"; 4 | 5 | export function Analytics() { 6 | return ; 7 | } 8 | -------------------------------------------------------------------------------- /apps/www/src/lib/stripe.ts: -------------------------------------------------------------------------------- 1 | import { env } from "@/env"; 2 | import Stripe from "stripe"; 3 | 4 | export const stripe = new Stripe(env.STRIPE_API_KEY, { 5 | apiVersion: "2023-10-16", 6 | typescript: true, 7 | }); 8 | -------------------------------------------------------------------------------- /apps/www/src/lib/validations/og.ts: -------------------------------------------------------------------------------- 1 | import * as z from "zod"; 2 | 3 | export const ogImageSchema = z.object({ 4 | heading: z.string(), 5 | type: z.string(), 6 | mode: z.enum(["light", "dark"]).default("dark"), 7 | }); 8 | -------------------------------------------------------------------------------- /tooling/tailwind/tailwind.d.ts: -------------------------------------------------------------------------------- 1 | declare module "tailwindcss/lib/util/flattenColorPalette" { 2 | export default function flattenColorPalette( 3 | pallette: Record, 4 | ): Record; 5 | } 6 | -------------------------------------------------------------------------------- /apps/api/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .wrangler 4 | .dev.vars 5 | .wrangler.toml 6 | wrangler.toml 7 | 8 | 9 | # Change them to your taste: 10 | package-lock.json 11 | yarn.lock 12 | pnpm-lock.yaml 13 | bun.lockb -------------------------------------------------------------------------------- /apps/www/src/app/api/auth/[...nextauth]/route.ts: -------------------------------------------------------------------------------- 1 | import { authOptions } from "@/lib/auth"; 2 | import NextAuth from "next-auth"; 3 | 4 | const handler = NextAuth(authOptions); 5 | 6 | export { handler as GET, handler as POST }; 7 | -------------------------------------------------------------------------------- /packages/ui/src/components/aspect-ratio.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio"; 4 | 5 | const AspectRatio = AspectRatioPrimitive.Root; 6 | 7 | export { AspectRatio }; 8 | -------------------------------------------------------------------------------- /packages/ui/src/utils.ts: -------------------------------------------------------------------------------- 1 | import type { ClassValue } from "clsx"; 2 | import { clsx } from "clsx"; 3 | import { twMerge } from "tailwind-merge"; 4 | 5 | export function cn(...inputs: ClassValue[]) { 6 | return twMerge(clsx(inputs)); 7 | } 8 | -------------------------------------------------------------------------------- /tooling/eslint/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@dingify/tsconfig/base.json", 3 | "compilerOptions": { 4 | "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json" 5 | }, 6 | "include": ["."], 7 | "exclude": ["node_modules"] 8 | } 9 | -------------------------------------------------------------------------------- /apps/www/src/app/(auth)/layout.tsx: -------------------------------------------------------------------------------- 1 | interface AuthLayoutProps { 2 | children: React.ReactNode; 3 | } 4 | 5 | export default function AuthLayout({ children }: AuthLayoutProps) { 6 | return
{children}
; 7 | } 8 | -------------------------------------------------------------------------------- /apps/www/src/app/robots.ts: -------------------------------------------------------------------------------- 1 | import { MetadataRoute } from "next"; 2 | 3 | export default function robots(): MetadataRoute.Robots { 4 | return { 5 | rules: { 6 | userAgent: "*", 7 | allow: "/", 8 | }, 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /apps/www/src/lib/crypto.ts: -------------------------------------------------------------------------------- 1 | export function generateApiKey() { 2 | const array = new Uint8Array(32); 3 | crypto.getRandomValues(array); 4 | return Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join( 5 | "", 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/db/eslint.config.js: -------------------------------------------------------------------------------- 1 | import baseConfig from "@dingify/eslint-config/base"; 2 | 3 | /** @type {import('typescript-eslint').Config} */ 4 | export default [ 5 | { 6 | ignores: ["dist/**"], 7 | }, 8 | ...baseConfig, 9 | ]; 10 | -------------------------------------------------------------------------------- /tooling/prettier/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@dingify/tsconfig/base.json", 3 | "compilerOptions": { 4 | "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json" 5 | }, 6 | "include": ["."], 7 | "exclude": ["node_modules"] 8 | } 9 | -------------------------------------------------------------------------------- /tooling/tailwind/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@dingify/tsconfig/base.json", 3 | "compilerOptions": { 4 | "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json" 5 | }, 6 | "include": ["."], 7 | "exclude": ["node_modules"] 8 | } 9 | -------------------------------------------------------------------------------- /apps/api/src/lib/generateApiKey.ts: -------------------------------------------------------------------------------- 1 | export function generateApiKey() { 2 | const array = new Uint8Array(32); 3 | crypto.getRandomValues(array); 4 | return Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join( 5 | "" 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /apps/www/src/app/(docs)/guides/layout.tsx: -------------------------------------------------------------------------------- 1 | interface GuidesLayoutProps { 2 | children: React.ReactNode; 3 | } 4 | 5 | export default function GuidesLayout({ children }: GuidesLayoutProps) { 6 | return
{children}
; 7 | } 8 | -------------------------------------------------------------------------------- /apps/www/src/lib/session.ts: -------------------------------------------------------------------------------- 1 | import { authOptions } from "@/lib/auth"; 2 | import { getServerSession } from "next-auth/next"; 3 | 4 | export async function getCurrentUser() { 5 | const session = await getServerSession(authOptions); 6 | 7 | return session?.user; 8 | } 9 | -------------------------------------------------------------------------------- /apps/www/src/hooks/use-mounted.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export function useMounted() { 4 | const [mounted, setMounted] = React.useState(false); 5 | 6 | React.useEffect(() => { 7 | setMounted(true); 8 | }, []); 9 | 10 | return mounted; 11 | } 12 | -------------------------------------------------------------------------------- /packages/db/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@dingify/tsconfig/internal-package.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json" 6 | }, 7 | "include": ["."], 8 | "exclude": ["node_modules", "dist"] 9 | } 10 | -------------------------------------------------------------------------------- /tooling/typescript/internal-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "./base.json", 4 | "compilerOptions": { 5 | "declaration": true, 6 | "declarationMap": true, 7 | "noEmit": false, 8 | "emitDeclarationOnly": true 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@dingify/tsconfig/internal-package.json", 3 | "compilerOptions": { 4 | "lib": ["dom", "dom.iterable", "ES2022"], 5 | "jsx": "preserve", 6 | "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json" 7 | }, 8 | "include": ["*.ts", "src"], 9 | "exclude": ["node_modules"] 10 | } 11 | -------------------------------------------------------------------------------- /tooling/tailwind/eslint.config.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import baseConfig from "@dingify/eslint-config/base"; 4 | 5 | export default [ 6 | ...baseConfig, 7 | // TODO: Resolve errors when setting these rules to 'error' 8 | { rules: { "@typescript-eslint/ban-ts-comment": "warn" } }, 9 | ]; 10 | -------------------------------------------------------------------------------- /apps/www/src/content/docs/in-progress.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Not Implemented 3 | description: This page is in progress. 4 | --- 5 | 6 | 7 | 8 | This site is a work in progress. If you see dummy text on a page, it means I'm still working on it. You can follow updates on Twitter [@shadcn](https://twitter.com/shadcn). 9 | 10 | 11 | -------------------------------------------------------------------------------- /packages/ui/eslint.config.js: -------------------------------------------------------------------------------- 1 | import baseConfig from "@dingify/eslint-config/base"; 2 | import reactConfig from "@dingify/eslint-config/react"; 3 | 4 | /** @type {import('typescript-eslint').Config} */ 5 | const config = [ 6 | { 7 | ignores: [], 8 | }, 9 | ...baseConfig, 10 | ...reactConfig, 11 | ]; 12 | 13 | export default config; 14 | -------------------------------------------------------------------------------- /apps/api/src/env.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/consistent-type-definitions */ 2 | import { z } from "zod"; 3 | 4 | export const zEnv = z.object({ 5 | DATABASE_URL: z.string(), 6 | ENVIRONMENT: z 7 | .enum(["development", "preview", "production"]) 8 | .default("development"), 9 | }); 10 | 11 | export type Env = z.infer; 12 | -------------------------------------------------------------------------------- /packages/ui/src/components/skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "../utils"; 2 | 3 | function Skeleton({ 4 | className, 5 | ...props 6 | }: React.HTMLAttributes) { 7 | return ( 8 |
12 | ); 13 | } 14 | 15 | export { Skeleton }; 16 | -------------------------------------------------------------------------------- /apps/www/src/types/next-auth.d.ts: -------------------------------------------------------------------------------- 1 | import { User } from "next-auth"; 2 | import { JWT } from "next-auth/jwt"; 3 | 4 | type UserId = string; 5 | 6 | declare module "next-auth/jwt" { 7 | interface JWT { 8 | id: UserId; 9 | } 10 | } 11 | 12 | declare module "next-auth" { 13 | interface Session { 14 | user: User & { 15 | id: UserId; 16 | }; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /apps/api/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "moduleResolution": "Bundler", 6 | "strict": true, 7 | "skipLibCheck": true, 8 | "lib": [ 9 | "ESNext" 10 | ], 11 | "types": [ 12 | "@cloudflare/workers-types" 13 | ], 14 | "jsx": "react-jsx", 15 | "jsxImportSource": "hono/jsx" 16 | }, 17 | } -------------------------------------------------------------------------------- /apps/www/src/hooks/use-signin-modal.ts: -------------------------------------------------------------------------------- 1 | import { create } from "zustand"; 2 | 3 | interface useSigninModalStore { 4 | isOpen: boolean; 5 | onOpen: () => void; 6 | onClose: () => void; 7 | } 8 | 9 | export const useSigninModal = create((set) => ({ 10 | isOpen: false, 11 | onOpen: () => set({ isOpen: true }), 12 | onClose: () => set({ isOpen: false }), 13 | })); 14 | -------------------------------------------------------------------------------- /apps/www/src/hooks/use-language-modal.ts: -------------------------------------------------------------------------------- 1 | import { create } from "zustand"; 2 | 3 | interface UseLanguageModalStore { 4 | isOpen: boolean; 5 | onOpen: () => void; 6 | onClose: () => void; 7 | } 8 | 9 | export const useLanguageModal = create((set) => ({ 10 | isOpen: false, 11 | onOpen: () => set({ isOpen: true }), 12 | onClose: () => set({ isOpen: false }), 13 | })); 14 | -------------------------------------------------------------------------------- /apps/api/src/validators/types.ts: -------------------------------------------------------------------------------- 1 | interface User { 2 | id: number; 3 | name?: string; 4 | email: string; 5 | plan: string; 6 | events: Event[]; 7 | createdAt: Date; 8 | } 9 | 10 | interface Event { 11 | id: number; 12 | channel: string; 13 | event: string; 14 | userId: number; 15 | icon: string; 16 | notify: boolean; 17 | tags: Record; 18 | createdAt: Date; 19 | } 20 | -------------------------------------------------------------------------------- /apps/www/src/components/users/UserCard.tsx: -------------------------------------------------------------------------------- 1 | import UserCardsSection from "./UserCardsSection"; 2 | import { UserMainSection } from "./UserMainSection"; 3 | 4 | export default function UserCard({ customerDetails }) { 5 | return ( 6 |
7 | 8 | 9 |
10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /packages/ui/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is not used for any compilation purpose, it is only used 3 | * for Tailwind Intellisense & Autocompletion in the source files 4 | */ 5 | import type { Config } from "tailwindcss"; 6 | 7 | import baseConfig from "@dingify/tailwind-config"; 8 | 9 | export default { 10 | content: ["./src/**/*.tsx"], 11 | presets: [baseConfig], 12 | } satisfies Config; 13 | -------------------------------------------------------------------------------- /packages/ui/src/components/collapsible.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"; 4 | 5 | const Collapsible = CollapsiblePrimitive.Root; 6 | 7 | const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger; 8 | 9 | const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent; 10 | 11 | export { Collapsible, CollapsibleTrigger, CollapsibleContent }; 12 | -------------------------------------------------------------------------------- /apps/www/src/app/(property)/property/[id]/report/page.tsx: -------------------------------------------------------------------------------- 1 | export default function Report(props: { params: { id: string } }) { 2 | return ( 3 |
4 |

{"Nothing to see here..."}

5 | Nothing here yet 9 |
10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /apps/www/src/components/buttons/DocsButton.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | 3 | import { buttonVariants } from "@dingify/ui/components/button"; 4 | 5 | import { cn } from "@/lib/utils"; 6 | 7 | export function DocsButton() { 8 | return ( 9 | 13 | Explore Docs 14 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /apps/www/src/components/dashboard/shell.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { cn } from "@/lib/utils"; 3 | 4 | type DashboardShellProps = React.HTMLAttributes; 5 | 6 | export function DashboardShell({ 7 | children, 8 | className, 9 | ...props 10 | }: DashboardShellProps) { 11 | return ( 12 |
13 | {children} 14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /apps/www/src/hooks/use-lock-body.ts: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | // @see https://usehooks.com/useLockBodyScroll. 4 | export function useLockBody() { 5 | React.useLayoutEffect((): (() => void) => { 6 | const originalStyle: string = window.getComputedStyle( 7 | document.body, 8 | ).overflow; 9 | document.body.style.overflow = "hidden"; 10 | return () => (document.body.style.overflow = originalStyle); 11 | }, []); 12 | } 13 | -------------------------------------------------------------------------------- /apps/www/public/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Propwrite", 3 | "short_name": "Propwrite", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /apps/api/wrangler.toml: -------------------------------------------------------------------------------- 1 | name = "api" 2 | compatibility_date = "2023-12-28" 3 | main = "src/index.ts" 4 | 5 | [vars] 6 | MY_VAR = "my-variable" 7 | 8 | # [[kv_namespaces]] 9 | # binding = "MY_KV_NAMESPACE" 10 | # id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" 11 | 12 | # [[r2_buckets]] 13 | # binding = "MY_BUCKET" 14 | # bucket_name = "my-bucket" 15 | 16 | # [[d1_databases]] 17 | # binding = "DB" 18 | # database_name = "my-database" 19 | # database_id = "" 20 | 21 | # [ai] 22 | # binding = "AI" 23 | -------------------------------------------------------------------------------- /apps/www/src/components/UserBadge.tsx: -------------------------------------------------------------------------------- 1 | // components/shared/ClickableBadge.tsx 2 | import { Badge } from "@dingify/ui/components/badge"; 3 | 4 | export function UserBadge({ customerId, userId, variant, onClick }) { 5 | const handleClick = (e) => { 6 | onClick(customerId); 7 | }; 8 | 9 | return ( 10 | 15 | {userId} 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /apps/www/src/assets/fonts/index.ts: -------------------------------------------------------------------------------- 1 | import { Inter as FontSans, Urbanist } from "next/font/google"; 2 | import localFont from "next/font/local"; 3 | 4 | export const fontSans = FontSans({ 5 | subsets: ["latin"], 6 | variable: "--font-sans", 7 | }); 8 | 9 | export const fontUrban = Urbanist({ 10 | subsets: ["latin"], 11 | variable: "--font-urban", 12 | }); 13 | 14 | export const fontHeading = localFont({ 15 | src: "./CalSans-SemiBold.woff2", 16 | variable: "--font-heading", 17 | }); 18 | -------------------------------------------------------------------------------- /apps/www/src/actions/get-metrics-user.ts: -------------------------------------------------------------------------------- 1 | import { prisma } from "@/lib/db"; 2 | import { getCurrentUser } from "@/lib/session"; 3 | 4 | export async function getMetricsForUser() { 5 | const user = await getCurrentUser(); 6 | 7 | if (!user) { 8 | throw new Error("User not authenticated"); 9 | } 10 | 11 | const metrics = await prisma.metrics.findFirst({ 12 | where: { 13 | project: { 14 | userId: user.id, 15 | }, 16 | }, 17 | }); 18 | 19 | return metrics; 20 | } 21 | -------------------------------------------------------------------------------- /apps/www/src/app/(marketing)/error.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Button } from "@dingify/ui/components/button"; 4 | 5 | export default function Error({ reset }: { reset: () => void }) { 6 | return ( 7 |
8 |

Something went wrong!

9 | 12 |
13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /tooling/eslint/nextjs.js: -------------------------------------------------------------------------------- 1 | import nextPlugin from "@next/eslint-plugin-next"; 2 | 3 | /** @type {Awaited} */ 4 | export default [ 5 | { 6 | files: ["**/*.ts", "**/*.tsx"], 7 | plugins: { 8 | "@next/next": nextPlugin, 9 | }, 10 | rules: { 11 | ...nextPlugin.configs.recommended.rules, 12 | ...nextPlugin.configs["core-web-vitals"].rules, 13 | "@next/next/no-duplicate-head": "off", 14 | "react-hooks/exhaustive-deps": "off", 15 | }, 16 | }, 17 | ]; 18 | -------------------------------------------------------------------------------- /apps/www/src/hooks/use-scroll.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect, useState } from "react"; 2 | 3 | export default function useScroll(threshold: number) { 4 | const [scrolled, setScrolled] = useState(false); 5 | 6 | const onScroll = useCallback(() => { 7 | setScrolled(window.pageYOffset > threshold); 8 | }, [threshold]); 9 | 10 | useEffect(() => { 11 | window.addEventListener("scroll", onScroll); 12 | return () => window.removeEventListener("scroll", onScroll); 13 | }, [onScroll]); 14 | 15 | return scrolled; 16 | } 17 | -------------------------------------------------------------------------------- /apps/www/src/actions/get-notification-settings.ts: -------------------------------------------------------------------------------- 1 | // actions/get-notification-settings.ts 2 | "use server"; 3 | 4 | import { prisma } from "@/lib/db"; 5 | import { getCurrentUser } from "@/lib/session"; 6 | 7 | export async function getNotificationSettings() { 8 | const user = await getCurrentUser(); 9 | 10 | if (!user) { 11 | throw new Error("User not authenticated"); 12 | } 13 | 14 | const settings = await prisma.notificationSetting.findFirst({ 15 | where: { userId: user.id }, 16 | }); 17 | 18 | return settings; 19 | } 20 | -------------------------------------------------------------------------------- /apps/www/src/components/buttons/StartOnbordaButton.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useEffect } from "react"; 4 | import { useOnborda } from "onborda"; 5 | 6 | import { Button } from "@dingify/ui/components/button"; 7 | 8 | export function StartOnbordaButton() { 9 | const { startOnborda } = useOnborda(); 10 | const handleStartOnborda = () => { 11 | startOnborda(); 12 | }; 13 | 14 | return ( 15 | 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /apps/www/src/components/modal-provider.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { SignInModal } from "@/components/layout/sign-in-modal"; 4 | import { useMounted } from "@/hooks/use-mounted"; 5 | 6 | import { LanguageModal } from "./layout/language-modal"; 7 | 8 | export const ModalProvider = () => { 9 | const mounted = useMounted(); 10 | 11 | if (!mounted) { 12 | return null; 13 | } 14 | 15 | return ( 16 | <> 17 | 18 | 19 | {/* add your own modals here... */} 20 | 21 | ); 22 | }; 23 | -------------------------------------------------------------------------------- /apps/www/src/config/site.ts: -------------------------------------------------------------------------------- 1 | import type { SiteConfig } from "@/types"; 2 | import { env } from "@/env"; 3 | 4 | const site_url = env.NEXT_PUBLIC_APP_URL; 5 | 6 | export const siteConfig: SiteConfig = { 7 | name: "Dingify", 8 | description: 9 | "Dingify revolutionizes alearts and notifications for developers and businesses", 10 | url: site_url, 11 | ogImage: `${site_url}/og.jpg`, 12 | links: { 13 | twitter: "https://twitter.com/codehagen", 14 | github: "https://github.com/meglerhagen", 15 | }, 16 | mailSupport: "christer@sailsdock.com", 17 | }; 18 | -------------------------------------------------------------------------------- /packages/ui/src/components/stepper/use-media-query.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | export function useMediaQuery(query: string) { 4 | const [value, setValue] = React.useState(false); 5 | 6 | React.useEffect(() => { 7 | function onChange(event: MediaQueryListEvent) { 8 | setValue(event.matches); 9 | } 10 | 11 | const result = matchMedia(query); 12 | result.addEventListener("change", onChange); 13 | setValue(result.matches); 14 | 15 | return () => result.removeEventListener("change", onChange); 16 | }, [query]); 17 | 18 | return value; 19 | } -------------------------------------------------------------------------------- /apps/www/src/app/(dashboard)/dashboard/billing/loading.tsx: -------------------------------------------------------------------------------- 1 | import { DashboardHeader } from "@/components/dashboard/header"; 2 | import { DashboardShell } from "@/components/dashboard/shell"; 3 | import { CardSkeleton } from "@/components/shared/card-skeleton"; 4 | 5 | export default function DashboardBillingLoading() { 6 | return ( 7 | 8 | 12 |
13 | 14 |
15 |
16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /apps/www/src/app/(dashboard)/dashboard/settings/loading.tsx: -------------------------------------------------------------------------------- 1 | import { DashboardHeader } from "@/components/dashboard/header"; 2 | import { DashboardShell } from "@/components/dashboard/shell"; 3 | import { CardSkeleton } from "@/components/shared/card-skeleton"; 4 | 5 | export default function DashboardSettingsLoading() { 6 | return ( 7 | 8 | 12 |
13 | 14 |
15 |
16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /apps/www/src/app/(property)/property/[id]/settings/loading.tsx: -------------------------------------------------------------------------------- 1 | import { DashboardHeader } from "@/components/dashboard/header"; 2 | import { DashboardShell } from "@/components/dashboard/shell"; 3 | import { CardSkeleton } from "@/components/shared/card-skeleton"; 4 | 5 | export default function DashboardSettingsLoading() { 6 | return ( 7 | 8 | 12 |
13 | 14 |
15 |
16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /tooling/eslint/react.js: -------------------------------------------------------------------------------- 1 | import reactPlugin from "eslint-plugin-react"; 2 | import hooksPlugin from "eslint-plugin-react-hooks"; 3 | 4 | /** @type {Awaited} */ 5 | export default [ 6 | { 7 | files: ["**/*.ts", "**/*.tsx"], 8 | plugins: { 9 | react: reactPlugin, 10 | "react-hooks": hooksPlugin, 11 | }, 12 | rules: { 13 | ...reactPlugin.configs["jsx-runtime"].rules, 14 | ...hooksPlugin.configs.recommended.rules, 15 | }, 16 | languageOptions: { 17 | globals: { 18 | React: "writable", 19 | }, 20 | }, 21 | }, 22 | ]; 23 | -------------------------------------------------------------------------------- /apps/www/src/components/buttons/LanguageButton.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useLanguageModal } from "@/hooks/use-language-modal"; 4 | import { useSigninModal } from "@/hooks/use-signin-modal"; 5 | import { cn } from "@/lib/utils"; 6 | 7 | import { Button, buttonVariants } from "@dingify/ui/components/button"; 8 | 9 | export function LanugageButton({ userId }) { 10 | const signInModal = useLanguageModal(); 11 | 12 | return ( 13 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /apps/www/src/components/dashboard/header.tsx: -------------------------------------------------------------------------------- 1 | interface DashboardHeaderProps { 2 | heading: string; 3 | text?: string; 4 | children?: React.ReactNode; 5 | } 6 | 7 | export function DashboardHeader({ 8 | heading, 9 | text, 10 | children, 11 | }: DashboardHeaderProps) { 12 | return ( 13 |
14 |
15 |

{heading}

16 | {text &&

{text}

} 17 |
18 | {children} 19 |
20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /apps/www/src/lib/pixel-events.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React, { useEffect } from "react"; 4 | import { usePathname, useSearchParams } from "next/navigation"; 5 | 6 | export const FacebookPixelEvents: React.FC = () => { 7 | const pathname = usePathname(); 8 | const searchParams = useSearchParams(); 9 | 10 | useEffect(() => { 11 | import("react-facebook-pixel") 12 | .then((x) => x.default) 13 | .then((ReactPixel) => { 14 | ReactPixel.init("1442315969659116"); //don't 15 | ReactPixel.pageView(); 16 | }); 17 | }, [pathname, searchParams]); 18 | 19 | return null; 20 | }; 21 | -------------------------------------------------------------------------------- /apps/www/src/app/(dashboard)/dashboard/notifications/loading.tsx: -------------------------------------------------------------------------------- 1 | import { DashboardHeader } from "@/components/dashboard/header"; 2 | import { DashboardShell } from "@/components/dashboard/shell"; 3 | import { CardSkeleton } from "@/components/shared/card-skeleton"; 4 | 5 | export default function DashboardSettingsLoading() { 6 | return ( 7 | 8 | 12 |
13 | 14 |
15 |
16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /apps/www/src/components/shared/card-skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Card, 3 | CardContent, 4 | CardFooter, 5 | CardHeader, 6 | } from "@dingify/ui/components/card"; 7 | import { Skeleton } from "@dingify/ui/components/skeleton"; 8 | 9 | export function CardSkeleton() { 10 | return ( 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /apps/www/src/actions/delete-customer.ts: -------------------------------------------------------------------------------- 1 | // actions/delete-customer.ts 2 | "use server"; 3 | 4 | import { revalidatePath } from "next/cache"; 5 | 6 | import { prisma } from "@/lib/db"; 7 | 8 | export async function deleteCustomer(customerId) { 9 | try { 10 | await prisma.customer.delete({ 11 | where: { id: customerId }, 12 | }); 13 | 14 | // Revalidate the path after deleting the customer 15 | revalidatePath("/dashboard/users"); 16 | 17 | return { success: true }; 18 | } catch (error) { 19 | console.error("Error deleting customer:", error); 20 | return { success: false, error: error.message }; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /apps/www/src/config/marketing.ts: -------------------------------------------------------------------------------- 1 | import type { MarketingConfig } from "@/types"; 2 | 3 | export const marketingConfig: MarketingConfig = { 4 | mainNav: [ 5 | { 6 | title: "Docs", 7 | href: "https://docs.dingify.io/", 8 | }, 9 | { 10 | title: "Open Startup", 11 | href: "/open", 12 | }, 13 | { 14 | title: "Pricing", 15 | href: "/pricing", 16 | disabled: true, 17 | }, 18 | { 19 | title: "Blog", 20 | href: "/blog", 21 | disabled: true, 22 | }, 23 | // { 24 | // title: "Documentation", 25 | // href: "/docs", 26 | // disabled: true, 27 | // }, 28 | ], 29 | }; 30 | -------------------------------------------------------------------------------- /apps/www/src/components/properties/NoSummaryPlaceholder.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { EmptyPlaceholder } from "@/components/shared/empty-placeholder"; 4 | 5 | import { Button } from "@dingify/ui/components/button"; 6 | 7 | export default function NoSummaryPlaceholder({ propertyId, slug }) { 8 | return ( 9 | 10 | 11 | Upload pictures 12 | 13 | Upload pictures to start and let us create the text 14 | 15 | 21 | ); 22 | }; 23 | 24 | export default EmailButton; 25 | -------------------------------------------------------------------------------- /apps/www/src/components/users/UserPowerCard.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | import { 4 | Card, 5 | CardContent, 6 | CardDescription, 7 | CardHeader, 8 | CardTitle, 9 | } from "@dingify/ui/components/card"; 10 | 11 | export function UserPowerCard({ customerDetails }) { 12 | return ( 13 | 14 | 15 | Customer Status 16 | Health of the customer 17 | 18 | 19 |
20 | {customerDetails.userStatus} 21 |
22 |
23 |
24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /apps/api/src/routes/apiKeyMiddleware.ts: -------------------------------------------------------------------------------- 1 | import { Context } from "hono"; 2 | import { prisma } from "../lib/db"; 3 | import { Env } from "../env"; 4 | 5 | export async function apiKeyMiddleware( 6 | c: Context<{ 7 | Bindings: Env; 8 | }>, 9 | next: any, 10 | ) { 11 | const apiKey = c.req.header("x-api-key"); 12 | if (!apiKey) { 13 | return c.json({ ok: false, message: "API key is required" }, 401); 14 | } 15 | 16 | const user = await prisma(c.env).user.findUnique({ where: { apiKey } }); 17 | if (!user) { 18 | return c.json({ ok: false, message: "Invalid API key" }, 401); 19 | } 20 | 21 | // Store user in context for downstream use 22 | c.set("user", user); 23 | return next(); 24 | } 25 | -------------------------------------------------------------------------------- /apps/www/src/actions/get-user-details.ts: -------------------------------------------------------------------------------- 1 | // actions/get-user-data.ts 2 | "use server"; 3 | 4 | import { prisma } from "@/lib/db"; 5 | import { getCurrentUser } from "@/lib/session"; 6 | 7 | export async function getUserData() { 8 | const user = await getCurrentUser(); 9 | if (!user) { 10 | throw new Error("User not authenticated"); 11 | } 12 | 13 | const userData = await prisma.user.findUnique({ 14 | where: { id: user.id }, 15 | select: { 16 | apiKey: true, 17 | name: true, 18 | email: true, 19 | // add other fields you might need 20 | }, 21 | }); 22 | 23 | if (!userData) { 24 | throw new Error("User not found"); 25 | } 26 | 27 | return userData; 28 | } 29 | -------------------------------------------------------------------------------- /apps/www/src/app/(docs)/docs/layout.tsx: -------------------------------------------------------------------------------- 1 | import { DocsSidebarNav } from "@/components/docs/sidebar-nav"; 2 | import { docsConfig } from "@/config/docs"; 3 | 4 | interface DocsLayoutProps { 5 | children: React.ReactNode; 6 | } 7 | 8 | export default function DocsLayout({ children }: DocsLayoutProps) { 9 | return ( 10 |
11 | 14 | {children} 15 |
16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /apps/www/src/components/docs/page-header.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils"; 2 | 3 | interface DocsPageHeaderProps extends React.HTMLAttributes { 4 | heading: string; 5 | text?: string; 6 | } 7 | 8 | export function DocsPageHeader({ 9 | heading, 10 | text, 11 | className, 12 | ...props 13 | }: DocsPageHeaderProps) { 14 | return ( 15 | <> 16 |
17 |

18 | {heading} 19 |

20 | {text &&

{text}

} 21 |
22 |
23 | 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /tooling/prettier/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@dingify/prettier-config", 3 | "private": true, 4 | "version": "0.1.0", 5 | "type": "module", 6 | "exports": { 7 | ".": "./index.js" 8 | }, 9 | "scripts": { 10 | "clean": "rm -rf .turbo node_modules", 11 | "format": "prettier --check . --ignore-path ../../.gitignore", 12 | "typecheck": "tsc --noEmit" 13 | }, 14 | "dependencies": { 15 | "@ianvs/prettier-plugin-sort-imports": "^4.2.1", 16 | "prettier": "^3.2.5", 17 | "prettier-plugin-tailwindcss": "^0.5.13" 18 | }, 19 | "devDependencies": { 20 | "@dingify/tsconfig": "workspace:*", 21 | "typescript": "^5.4.5" 22 | }, 23 | "prettier": "@dingify/prettier-config" 24 | } 25 | -------------------------------------------------------------------------------- /tooling/typescript/base.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "compilerOptions": { 4 | "esModuleInterop": true, 5 | "skipLibCheck": true, 6 | "target": "ES2022", 7 | "lib": ["ES2022"], 8 | "allowJs": true, 9 | "resolveJsonModule": true, 10 | "moduleDetection": "force", 11 | "isolatedModules": true, 12 | "incremental": true, 13 | "disableSourceOfProjectReferenceRedirect": true, 14 | "strict": true, 15 | "noUncheckedIndexedAccess": true, 16 | "checkJs": true, 17 | "module": "Preserve", 18 | "moduleResolution": "Bundler", 19 | "noEmit": true 20 | }, 21 | "exclude": ["node_modules", "build", "dist", ".next", ".expo"] 22 | } 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | .pnp 6 | .pnp.js 7 | 8 | # turbo 9 | .turbo/ 10 | .turbo/ 11 | 12 | # testing 13 | coverage 14 | 15 | # next.js 16 | .next/ 17 | out/ 18 | 19 | # build 20 | build 21 | dist 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | .pnpm-debug.log* 32 | 33 | # local env files 34 | .env*.local 35 | .env 36 | 37 | # vercel 38 | .vercel 39 | 40 | # typescript 41 | *.tsbuildinfo 42 | next-env.d.ts 43 | 44 | # email 45 | .react-email/ 46 | 47 | .vscode 48 | .contentlayer 49 | 50 | #API 51 | .wrangler.toml 52 | wrangler.toml 53 | -------------------------------------------------------------------------------- /apps/www/src/actions/change-customer-details.ts: -------------------------------------------------------------------------------- 1 | // actions/update-customer.ts 2 | "use server"; 3 | 4 | import { revalidatePath } from "next/cache"; 5 | 6 | import { prisma } from "@/lib/db"; 7 | 8 | export async function changeCustomerDetails(customerId, data) { 9 | try { 10 | const updatedCustomer = await prisma.customer.update({ 11 | where: { id: customerId }, 12 | data, 13 | }); 14 | 15 | // Revalidate the path after updating the customer 16 | revalidatePath("/dashboard/users"); 17 | 18 | return { success: true, customer: updatedCustomer }; 19 | } catch (error) { 20 | console.error("Error updating customer:", error); 21 | return { success: false, error: error.message }; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | # Expo doesn't play nice with pnpm by default. 2 | # The symbolic links of pnpm break the rules of Expo monorepos. 3 | # @link https://docs.expo.dev/guides/monorepos/#common-issues 4 | node-linker=hoisted 5 | 6 | # In order to cache Prisma correctly 7 | public-hoist-pattern[]=*prisma* 8 | 9 | # FIXME: @prisma/client is required by the @acme/auth, 10 | # but we don't want it installed there since it's already 11 | # installed in the @acme/db package 12 | strict-peer-dependencies=false 13 | 14 | # Prevent pnpm from adding the "workspace:"" prefix to local 15 | # packages as it casues issues with manypkg 16 | # @link https://pnpm.io/npmrc#prefer-workspace-packages 17 | save-workspace-protocol=false 18 | prefer-workspace-packages=true 19 | -------------------------------------------------------------------------------- /apps/www/src/components/open-page/OpenStartupSection.tsx: -------------------------------------------------------------------------------- 1 | export default function OpenStartupSection() { 2 | return ( 3 |
4 |
5 |

6 | Open startup 7 |

8 |

9 | Everything Open! 10 |

11 |

12 | We want to make everything open, so lets share everything we have. 13 |

14 |
15 |
16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /apps/www/src/components/open-page/OpenUsersText.tsx: -------------------------------------------------------------------------------- 1 | import { Separator } from "@dingify/ui/components/separator"; 2 | 3 | export default function OpenUsersText() { 4 | return ( 5 |
6 |
7 | {/*

8 | Open startup 9 |

*/} 10 |

11 | Our users 12 |

13 |

14 | How is the growth? 15 |

16 |
17 |
18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /apps/www/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@dingify/tsconfig/base.json", 3 | "compilerOptions": { 4 | "lib": ["es2022", "dom", "dom.iterable"], 5 | "jsx": "preserve", 6 | "baseUrl": ".", 7 | "paths": { 8 | "@/*": ["./src/*"], 9 | "contentlayer/generated": ["./.contentlayer/generated"] 10 | }, 11 | "plugins": [{ "name": "next" }], 12 | "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json", 13 | "module": "esnext", 14 | "noImplicitAny": false, // TODO: Resolve errors when setting this to 'true' 15 | "useUnknownInCatchVariables": false // TODO: Resolve errors when setting this to 'true' 16 | }, 17 | "include": ["src", ".next/types/**/*.ts"], 18 | "exclude": ["node_modules", ".contentlayer/generated"] 19 | } 20 | -------------------------------------------------------------------------------- /apps/www/src/hooks/use-local-storage.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | 3 | const useLocalStorage = ( 4 | key: string, 5 | initialValue: T, 6 | ): [T, (value: T) => void] => { 7 | const [storedValue, setStoredValue] = useState(initialValue); 8 | 9 | useEffect(() => { 10 | // Retrieve from localStorage 11 | const item = window.localStorage.getItem(key); 12 | if (item) { 13 | setStoredValue(JSON.parse(item)); 14 | } 15 | }, [key]); 16 | 17 | const setValue = (value: T) => { 18 | // Save state 19 | setStoredValue(value); 20 | // Save to localStorage 21 | window.localStorage.setItem(key, JSON.stringify(value)); 22 | }; 23 | return [storedValue, setValue]; 24 | }; 25 | 26 | export default useLocalStorage; 27 | -------------------------------------------------------------------------------- /.github/workflows/take-issue.yml: -------------------------------------------------------------------------------- 1 | # .github/workflows/take.yml 2 | name: Assign issue to contributor 3 | on: 4 | issue_comment: 5 | 6 | jobs: 7 | assign: 8 | name: Take an issue 9 | runs-on: ubuntu-latest 10 | permissions: 11 | issues: write 12 | steps: 13 | - name: take the issue 14 | uses: bdougie/take-action@main 15 | with: 16 | message: 17 | Thanks for taking this issue! Let us know if you have any questions! 18 | blockingLabels: in progress 19 | blockingLabelsMessage: 20 | This issue is already taken by someone else and is in progress. You 21 | can find another one by surfing the issues page. 22 | trigger: .take 23 | token: ${{ secrets.GITHUB_TOKEN }} 24 | -------------------------------------------------------------------------------- /apps/www/src/app/(property)/property/[id]/loading.tsx: -------------------------------------------------------------------------------- 1 | import { DashboardHeader } from "@/components/dashboard/header"; 2 | import { DashboardShell } from "@/components/dashboard/shell"; 3 | import { CardSkeleton } from "@/components/shared/card-skeleton"; 4 | 5 | import { Button } from "@dingify/ui/components/button"; 6 | 7 | export default function DashboardLoading() { 8 | return ( 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 |
19 |
20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /apps/www/src/app/(dashboard)/dashboard/loading.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from "@dingify/ui/components/button"; 2 | 3 | import { DashboardHeader } from "@/components/dashboard/header"; 4 | import { DashboardShell } from "@/components/dashboard/shell"; 5 | import { CardSkeleton } from "@/components/shared/card-skeleton"; 6 | 7 | export default function DashboardLoading() { 8 | return ( 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 |
19 |
20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /apps/www/src/components/properties/NoPhotoPlaceholder copy.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Button } from "@dingify/ui/components/button"; 4 | 5 | import { EmptyPlaceholder } from "@/components/shared/empty-placeholder"; 6 | 7 | import { DocsButton } from "../buttons/DocsButton"; 8 | 9 | export default function NoPhotoPlaceholder() { 10 | return ( 11 | 12 | 13 | Your events 14 | 15 | Your events for this channel will appear here. 16 | 17 |
18 | 19 | 20 | 21 |
22 |
23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /apps/www/src/components/open-page/OpenMiddleSection.tsx: -------------------------------------------------------------------------------- 1 | import { Separator } from "@dingify/ui/components/separator"; 2 | 3 | export default function OpenMiddleSection() { 4 | return ( 5 |
6 |
7 | {/*

8 | Open startup 9 |

*/} 10 |

11 | Funding 12 |

13 |

14 | We dont have done any funding runs yet, should we? 15 |

16 |
17 | 18 |
19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /apps/api/src/lib/parsePrismaError.ts: -------------------------------------------------------------------------------- 1 | export function parsePrismaError(error: any): string { 2 | // A simple error message parser that looks for missing arguments 3 | const missingArgumentMatch = error.message.match( 4 | /Argument `(\w+)` is missing./ 5 | ); 6 | if (missingArgumentMatch) { 7 | let fieldName = missingArgumentMatch[1]; 8 | let baseMessage = `The '${fieldName}' field is required but was not provided.`; 9 | 10 | // Specific instructions for known fields 11 | if (fieldName === "channel") { 12 | baseMessage += " You need to add 'channel' to your call to make it work."; 13 | } 14 | // Add more specific messages for other fields if necessary 15 | return baseMessage; 16 | } 17 | // Default to returning the original error message if no known patterns are matched 18 | return error.message; 19 | } 20 | -------------------------------------------------------------------------------- /apps/www/src/app/(marketing)/layout.tsx: -------------------------------------------------------------------------------- 1 | import { Suspense } from "react"; 2 | 3 | import { marketingConfig } from "@/config/marketing"; 4 | import { getCurrentUser } from "@/lib/session"; 5 | import { NavBar } from "@/components/layout/navbar"; 6 | import { SiteFooter } from "@/components/layout/site-footer"; 7 | 8 | interface MarketingLayoutProps { 9 | children: React.ReactNode; 10 | } 11 | 12 | export default async function MarketingLayout({ 13 | children, 14 | }: MarketingLayoutProps) { 15 | const user = await getCurrentUser(); 16 | 17 | return ( 18 |
19 | 20 | 21 | 22 |
{children}
23 | 24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /apps/www/src/app/(marketing)/blog/page.tsx: -------------------------------------------------------------------------------- 1 | import { BlogPosts } from "@/components/blog-posts"; 2 | import { allPosts } from "contentlayer/generated"; 3 | import { compareDesc } from "date-fns"; 4 | 5 | export const metadata = { 6 | title: "Dingify Blog - Insights on Real-Time Monitoring & Analytics", 7 | description: 8 | "Explore the latest insights on real-time monitoring, data analytics, and journey tracking strategies in the Dingify blog. Stay ahead with innovative business analytics and industry trends.", 9 | }; 10 | 11 | export default async function BlogPage() { 12 | const posts = allPosts 13 | .filter((post) => post.published) 14 | .sort((a, b) => { 15 | return compareDesc(new Date(a.date), new Date(b.date)); 16 | }); 17 | 18 | return ( 19 |
20 | 21 |
22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /apps/www/src/actions/delete-event.ts: -------------------------------------------------------------------------------- 1 | "use server"; 2 | 3 | import { prisma } from "@/lib/db"; 4 | import { getCurrentUser } from "@/lib/session"; 5 | 6 | export async function deleteEvent(eventId) { 7 | const user = await getCurrentUser(); 8 | if (!user) { 9 | throw new Error("User not authenticated"); 10 | } 11 | 12 | const event = await prisma.event.findUnique({ 13 | where: { id: eventId }, 14 | include: { 15 | channel: { 16 | include: { 17 | project: true, 18 | }, 19 | }, 20 | }, 21 | }); 22 | 23 | if (!event || event.channel.project.userId !== user.id) { 24 | throw new Error( 25 | "Event not found or you don't have permission to delete this event", 26 | ); 27 | } 28 | 29 | await prisma.event.delete({ 30 | where: { id: eventId }, 31 | }); 32 | 33 | return { success: true }; 34 | } 35 | -------------------------------------------------------------------------------- /packages/ui/src/components/textarea.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { cn } from "../utils"; 4 | 5 | export type TextareaProps = React.TextareaHTMLAttributes; 6 | 7 | const Textarea = React.forwardRef( 8 | ({ className, ...props }, ref) => { 9 | return ( 10 |