GTBitsOfGood / heart-of-passion

Retreat Planning Tool for Heart of Passion
MIT License
5 stars 0 forks source link

[BOGHACKS] Donor History Page #124

Closed jaynagpaul closed 5 months ago

jaynagpaul commented 5 months ago

Description

For this ticket we want to add a new page which allows you to keep track of donors and the fundraisers they attended.

jaynagpaul commented 5 months ago

Oh no! I have no idea how to do my ticket :(

jaynagpaul commented 5 months ago

oh wait i know who might know how to do it!!!

jaynagpaul commented 5 months ago

samrat help

samrat-bot-app[bot] commented 5 months ago

Generating how-to...

samrat-bot-app[bot] commented 5 months ago

Here is the detailed response in markdown format:

Greeting

Hi Samrat! As a very talented senior developer working for the club Bits of Good, I'm excited to help you implement this ticket. Bits of Good is a club that builds and maintains open-source software for nonprofits, and I'm here to provide you with specific files to edit and code that should be written.

Ticket Details

Ticket Name: [BOGHACKS] Donor History Page

Files to Edit

next-env.d.ts

/// <reference types="next" />
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.

app.d.ts

type IUser = import("~/server/models/User").IUser;

/// <reference types="lucia" />
declare namespace Lucia {
  type Auth = import("./src/server/auth").Auth;

  type DatabaseUserAttributes = Omit<IUser, "_id">;
  type DatabaseSessionAttributes = {};
}

tailwind.config.ts

import { type Config } from "tailwindcss";

export default {
  content: ["./src/**/*.{js,ts,jsx,tsx}"],
  theme: {
    extend: {},
  },
  plugins: [],
} satisfies Config;

scripts/create-user.ts

import "dotenv/config";
import mongoose from "mongoose";

import readline from "readline";
import { roleSchema, Role } from "~/common/types";
import { auth } from "~/server/auth";

async function getUserOptions(): Promise<{
  name: string;
  email: string;
  role: Role;
}> {
  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
  });

  const name = await new Promise<string>((resolve) => {
    rl.question("Enter name: ", (answer) => {
      resolve(answer);
    });
  });

  const email = await new Promise<string>((resolve) => {
    rl.question("Enter email: ", (answer) => {
      resolve(answer);
    });
  });

  const role = await new Promise<string>((resolve) => {
    rl.question("Enter role: ", (answer) => {
      resolve(answer);
    });
  });

  rl.close();

  if (!roleSchema.parse(role)) {
    throw new Error("Invalid role");
  }

  return { name, email, role: role as Role };
}

(async () => {
  const { name, email, role } = await getUserOptions();
  let user = await auth.createUser({
    key: null, // To be created when they attempt to log in for the first time
    attributes: {
      name,
      role,
      email,
    },
  });
  mongoose.connection.close();
})();

scripts/generate-dummy-data.ts

import "dotenv/config";

import dbConnect from "../src/server/db";
import { ChapterModel } from "../src/server/models/Chapter";
import mongoose, { Types } from "mongoose";
import { RetreatModel } from "../src/server/models/Retreat";
import { EventModel } from "../src/server/models/Event";
import { FundraiserModel } from "../src/server/models/Fundraiser";

const generateData = async (chapterId: Types.ObjectId) => {
  var retreat = await RetreatModel.create({
    year: 2023,
    chapterId: chapterId,
  });

  var event1 = await EventModel.create({
    retreatId: retreat._id,
    name: "Yoga and Meditation Retreat",
    location: "Mountain Retreat Center",
    energyLevel: "high",
    dates: [
      { day: 1, from: "2:30 pm", to: "4:30 pm" },
      { day: 2, from: "5:30 pm", to: "6:30 pm" },
      { day: 3, from: "4:30 pm", to: "5:30 pm" },
      { day: 4, from: "11:30 am", to: "12:00 pm" },
    ],
    expenses: [
      {
        name: "Venue Rental",
        cost: 2000,
        type: "Miscellaneous",
        numUnits: 1,
      },
      {
        name: "Transportation",
        cost: 500,
        type: "Transportation",
        numUnits: 1,
      },
      {
        name: "Guest Speaker",
        cost: 1000,
        type: "Entertainment",
        numUnits: 2,
      },
    ],
  });
  var event2 = await EventModel.create({
    retreatId: retreat._id,
    name: "Art and Creativity Workshop",
    location: "City Art Center",
    energyLevel: "medium",
    dates: [
      { day: 1, from: "1:30 pm", to: "8:30 pm" },
      { day: 1, from: "1:45 pm", to: "2:00 pm" },
      { day: 1, from: "8:00 pm", to: "8:30 pm" },
      { day: 2, from: "3:30 pm", to: "6:30 pm" },
      { day: 3, from: "4:30 pm", to: "5:30 pm" },
      { day: 4, from: "11:30 am", to: "12:00 pm" },
    ],
    expenses: [
      {
        name: "Art Supplies",
        cost: 800,
        type: "Miscellaneous",
        numUnits: 1,
      },
      {
        name: "Instructor Fee",
        cost: 600,
        type: "Transportation",
        notes: "Professional art instructor.",
        numUnits: 1,
      },
    ],
  });

  var fundraiser1 = await FundraiserModel.create({
    retreatId: retreat._id,
    name: "School Fundraising Activity",
    location: "Georgia Tech",
    date: "2024-03-05",
    contactName: "J",
    email: "J@gmail.com",
    profit: 50000,
    expenses: [
      {
        name: "Preparation",
        cost: 400,
        type: "Miscellaneous",
        numUnits: 1,
      },
      {
        name: "Transportation",
        cost: 50,
        type: "Transportation",
        numUnits: 1,
      },
    ],
  });

  var fundraiser2 = await FundraiserModel.create({
    retreatId: retreat._id,
    name: "Museum Fundraising Activity",
    location: "High Museum",
    date: "2023-12-01",
    contactName: "Q",
    email: "Q@gmail.com",
    profit: 10000,
    expenses: [
      {
        name: "Preparation Materials",
        cost: 200,
        type: "Miscellaneous",
        numUnits: 1,
      },      
      {
        name: "Lunch",
        cost: 20,
        type: "Food",
        numUnits: 10,
      },
      {
        name: "Transportation",
        cost: 100,
        type: "Transportation",
        numUnits: 1,
      },
    ],
  });
  return retreat._id;
};

(async () => {
  await dbConnect();
  try {
    var chapter = await ChapterModel.create({
      name: "Test Chapter",
    });
    const id = await generateData(chapter._id as Types.ObjectId);
    console.log(`Retreat Generated: ${id}\nChapter Generated: ${chapter._id}`);
  } catch (e) {
    await ChapterModel.findOneAndDelete({ name: "Test Chapter" });
    var chapter = await ChapterModel.create({
      name: "Test Chapter",
    });
    const id = await generateData(chapter._id as Types.ObjectId);
    console.log(`Retreat Generated: ${id}\nChapter Generated: ${chapter._id}`);
  }
  mongoose.connection.close();
})();

styles/global.css

.no-scroll-bar::-webkit-scrollbar {
    display: none;
}

.no-scroll-bar {
    -ms-overflow-style: none;
    scrollbar-width: none;
}

src/server/auth.ts


import { lucia } from "lucia";
import { nextjs_future } from "lucia/middleware";
import { mongoose } from "@lucia-auth/adapter-mongoose";
import { google } from "@lucia-auth/oauth/providers";
import { UserModel } from "./models/User";
import { UserKeyModel } from "./models/UserKey";

// Polyfill for Node.js < 20
import "lucia/polyfill/node";
import dbConnect from "./db";
import { SessionModel } from "./models/Session";

export const auth = lucia({
  env: process.env.NODE_ENV == "development" ? "DEV" : "PROD",
  middleware: nextjs_future(),
  adapter: mongoose({
    User: UserModel,
    Key: UserKeyModel,
    Session: SessionModel,
  }),

  getUserAttributes: (data) => {
    return {
      _id: data.id,
      ...data,
    };
  },

  csrfProtection: false,
});

let baseUrl = process.env.BASE_URL ?? "http://localhost:3000";

// We set defaults so that developers don't need to setup oauth in development
export const googleAuth = google(auth, {
  clientId: process.env.GOOGLE_CLIENT_ID ?? "",
  clientSecret: process
jaynagpaul commented 5 months ago

Oh my god thanks samrat!!!

SamratSahoo commented 5 months ago

wowwwwww