Skip to content

Commit

Permalink
Support for multiple teams in single conversation (#51)
Browse files Browse the repository at this point in the history
  • Loading branch information
harishmohanraj authored Dec 1, 2023
1 parent 0fa539f commit 956689b
Show file tree
Hide file tree
Showing 13 changed files with 776 additions and 458 deletions.
39 changes: 21 additions & 18 deletions main.wasp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ app chatApp {
("markdown-to-jsx", "7.3.2"),
],
webSocket: {
fn: import { webSocketFn } from "@server/webSocket.js"
fn: import { checkTeamStatusAndUpdateInDB } from "@server/webSocket.js"
},
}

Expand Down Expand Up @@ -128,15 +128,19 @@ entity Chat {=psl
psl=}

entity Conversation {=psl
id Int @id @default(autoincrement())
conversation Json
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
status String?
chat Chat? @relation(fields: [chatId], references: [id])
chatId Int?
user User? @relation(fields: [userId], references: [id])
userId Int?
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
message String
role String
team_id Int?
team_name String?
team_status String?
is_question_from_agent Boolean @default(false)
chat Chat? @relation(fields: [chatId], references: [id])
chatId Int?
user User? @relation(fields: [userId], references: [id])
userId Int?
psl=}


Expand Down Expand Up @@ -208,12 +212,6 @@ page ChatPage {

// 📝 Actions aka Mutations

action generateGptResponse {
fn: import { generateGptResponse } from "@server/actions.js",
// entities: [User, RelatedObject]
entities: [User]
}

action stripePayment {
fn: import { stripePayment } from "@server/actions.js",
entities: [User]
Expand All @@ -224,8 +222,13 @@ action createChat {
entities: [Chat, Conversation]
}

action updateConversation {
fn: import { updateConversation } from "@server/actions.js",
action addNewConversationToChat {
fn: import { addNewConversationToChat } from "@server/actions.js",
entities: [Chat, Conversation]
}

action updateExistingConversation {
fn: import { updateExistingConversation } from "@server/actions.js",
entities: [Chat, Conversation]
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
Warnings:
- You are about to drop the column `conversation` on the `Conversation` table. All the data in the column will be lost.
- You are about to drop the column `status` on the `Conversation` table. All the data in the column will be lost.
- Added the required column `message` to the `Conversation` table without a default value. This is not possible if the table is not empty.
- Added the required column `role` to the `Conversation` table without a default value. This is not possible if the table is not empty.
*/
-- AlterTable
ALTER TABLE "Conversation" DROP COLUMN "conversation",
DROP COLUMN "status",
ADD COLUMN "is_question_from_agent" BOOLEAN NOT NULL DEFAULT false,
ADD COLUMN "message" TEXT NOT NULL,
ADD COLUMN "role" TEXT NOT NULL,
ADD COLUMN "team_id" INTEGER,
ADD COLUMN "team_name" TEXT,
ADD COLUMN "team_status" TEXT;
90 changes: 59 additions & 31 deletions src/client/AccountPage.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,36 @@
import { User } from '@wasp/entities';
import { useQuery } from '@wasp/queries'
import { User } from "@wasp/entities";
import { useQuery } from "@wasp/queries";
// import getRelatedObjects from '@wasp/queries/getRelatedObjects'
import logout from '@wasp/auth/logout';
import stripePayment from '@wasp/actions/stripePayment';
import { useState, Dispatch, SetStateAction } from 'react';
import logout from "@wasp/auth/logout";
import stripePayment from "@wasp/actions/stripePayment";
import { useState, Dispatch, SetStateAction } from "react";

// get your own link from your stripe dashboard: https://dashboard.stripe.com/settings/billing/portal
const CUSTOMER_PORTAL_LINK = 'https://billing.stripe.com/p/login/test_8wM8x17JN7DT4zC000';
const CUSTOMER_PORTAL_LINK =
"https://billing.stripe.com/p/login/test_8wM8x17JN7DT4zC000";

export default function Example({ user }: { user: User }) {
const [isLoading, setIsLoading] = useState<boolean>(false);

// const { data: relatedObjects, isLoading: isLoadingRelatedObjects } = useQuery(getRelatedObjects)

return (
<div className='mt-10 px-6 mx-auto w-auto md:w-1/2'>
<div className='overflow-hidden bg-white ring-1 ring-gray-900/10 shadow-lg sm:rounded-lg m-8 '>
<div className='px-4 py-5 sm:px-6 lg:px-8'>
<h3 className='text-base font-semibold leading-6 text-gray-900'>Account Information</h3>
<div className="mt-10 px-6 mx-auto w-auto md:w-1/2">
<div className="overflow-hidden bg-white ring-1 ring-gray-900/10 shadow-lg sm:rounded-lg m-8 ">
<div className="px-4 py-5 sm:px-6 lg:px-8">
<h3 className="text-base font-semibold leading-6 text-gray-900">
Account Information
</h3>
</div>
<div className='border-t border-gray-200 px-4 py-5 sm:p-0'>
<dl className='sm:divide-y sm:divide-gray-200'>
<div className='py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6'>
<dt className='text-sm font-medium text-gray-500'>Email address</dt>
<dd className='mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0'>{user.email}</dd>
<div className="border-t border-gray-200 px-4 py-5 sm:p-0">
<dl className="sm:divide-y sm:divide-gray-200">
<div className="py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6">
<dt className="text-sm font-medium text-gray-500">
Email address
</dt>
<dd className="mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0">
{user.email}
</dd>
</div>
{/* <div className='py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6'>
<dt className='text-sm font-medium text-gray-500'>Your Plan</dt>
Expand All @@ -46,8 +53,8 @@ export default function Example({ user }: { user: User }) {
<dd className='mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0'>I'm a cool customer.</dd>
</div> */}
{/* <div className='py-4 sm:grid sm:grid-cols-3 sm:gap-4 sm:py-5 sm:px-6'> */}
{/* <dt className='text-sm font-medium text-gray-500'>Most Recent User RelatedObject</dt> */}
{/* <dd className='mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0'>
{/* <dt className='text-sm font-medium text-gray-500'>Most Recent User RelatedObject</dt> */}
{/* <dd className='mt-1 text-sm text-gray-900 sm:col-span-2 sm:mt-0'>
{!!relatedObjects && relatedObjects.length > 0
? relatedObjects[relatedObjects.length - 1].content
: "You don't have any at this time."}
Expand All @@ -56,10 +63,10 @@ export default function Example({ user }: { user: User }) {
</dl>
</div>
</div>
<div className='inline-flex w-full justify-end'>
<div className="inline-flex w-full justify-end">
<button
onClick={logout}
className='inline-flex justify-center mx-8 py-2 px-4 border border-transparent shadow-md text-sm font-medium rounded-md text-white bg-captn-cta-red hover:bg-captn-cta-red-hover focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500'
className="inline-flex justify-center mx-8 py-2 px-4 border border-transparent shadow-md text-sm font-medium rounded-md text-white bg-captn-cta-red hover:bg-captn-cta-red-hover focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
>
logout
</button>
Expand All @@ -68,42 +75,63 @@ export default function Example({ user }: { user: User }) {
);
}

function BuyMoreButton({ isLoading, setIsLoading }: { isLoading: boolean, setIsLoading: Dispatch<SetStateAction<boolean>> }) {
function BuyMoreButton({
isLoading,
setIsLoading,
}: {
isLoading: boolean;
setIsLoading: Dispatch<SetStateAction<boolean>>;
}) {
const handleClick = async () => {
try {
setIsLoading(true);
const stripeResults = await stripePayment();
if (stripeResults?.sessionUrl) {
window.open(stripeResults.sessionUrl, '_self');
window.open(stripeResults.sessionUrl, "_self");
}

} catch (error: any) {
alert(error?.message ?? 'Something went wrong.')
alert(error?.message ?? "Something went wrong.");
} finally {
setIsLoading(false);
}
};

return (
<div className='ml-4 flex-shrink-0 sm:col-span-1 sm:mt-0'>
<button onClick={handleClick} className={`font-medium text-sm text-indigo-600 hover:text-indigo-500 ${isLoading && 'animate-pulse'}`}>
{!isLoading ? 'Buy More/Upgrade' : 'Loading...'}
<div className="ml-4 flex-shrink-0 sm:col-span-1 sm:mt-0">
<button
onClick={handleClick}
className={`font-medium text-sm text-indigo-600 hover:text-indigo-500 ${
isLoading && "animate-pulse"
}`}
>
{!isLoading ? "Buy More/Upgrade" : "Loading..."}
</button>
</div>
);
}

function CustomerPortalButton({ isLoading, setIsLoading }: { isLoading: boolean, setIsLoading: Dispatch<SetStateAction<boolean>> }) {
function CustomerPortalButton({
isLoading,
setIsLoading,
}: {
isLoading: boolean;
setIsLoading: Dispatch<SetStateAction<boolean>>;
}) {
const handleClick = () => {
setIsLoading(true);
window.open(CUSTOMER_PORTAL_LINK, '_blank');
window.open(CUSTOMER_PORTAL_LINK, "_blank");
setIsLoading(false);
};

return (
<div className='ml-4 flex-shrink-0 sm:col-span-1 sm:mt-0'>
<button onClick={handleClick} className={`font-medium text-sm text-indigo-600 hover:text-indigo-500 ${isLoading && 'animate-pulse'}`}>
{!isLoading ? 'Manage Subscription' : 'Loading...'}
<div className="ml-4 flex-shrink-0 sm:col-span-1 sm:mt-0">
<button
onClick={handleClick}
className={`font-medium text-sm text-indigo-600 hover:text-indigo-500 ${
isLoading && "animate-pulse"
}`}
>
{!isLoading ? "Manage Subscription" : "Loading..."}
</button>
</div>
);
Expand Down
81 changes: 49 additions & 32 deletions src/client/PricingPage.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
import { AiOutlineCheck } from 'react-icons/ai';
import stripePayment from '@wasp/actions/stripePayment';
import { useState } from 'react';
import { AiOutlineCheck } from "react-icons/ai";
import stripePayment from "@wasp/actions/stripePayment";
import { useState } from "react";

const prices = [
{
name: 'Credits',
id: 'credits',
href: '',
price: '$2.95',
description: 'Buy credits to use for your projects.',
features: ['10 credits', 'Use them any time', 'No expiration date'],
name: "Credits",
id: "credits",
href: "",
price: "$2.95",
description: "Buy credits to use for your projects.",
features: ["10 credits", "Use them any time", "No expiration date"],
disabled: true,
},
{
name: 'Monthly Subscription',
id: 'monthly',
href: '#',
priceMonthly: '$9.99',
description: 'Get unlimited usage for your projects.',
features: ['Unlimited usage of all features', 'Priority support', 'Cancel any time'],
name: "Monthly Subscription",
id: "monthly",
href: "#",
priceMonthly: "$9.99",
description: "Get unlimited usage for your projects.",
features: [
"Unlimited usage of all features",
"Priority support",
"Cancel any time",
],
},
];

Expand All @@ -30,43 +34,55 @@ export default function PricingPage() {
try {
const response = await stripePayment();
if (response?.sessionUrl) {
window.open(response.sessionUrl, '_self');
window.open(response.sessionUrl, "_self");
}
} catch (e) {
alert('Something went wrong. Please try again.');
alert("Something went wrong. Please try again.");
console.error(e);
} finally {
setIsLoading(false);
}
};


return (
<div className='mt-10 pb-24 sm:pb-32'>
<div className='mx-auto max-w-7xl px-6 lg:px-8'>
<div className='mx-auto grid max-w-md grid-cols-1 gap-8 lg:max-w-4xl lg:grid-cols-2'>
<div className="mt-10 pb-24 sm:pb-32">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<div className="mx-auto grid max-w-md grid-cols-1 gap-8 lg:max-w-4xl lg:grid-cols-2">
{prices.map((price) => (
<div
key={price.id}
className='flex flex-col justify-between rounded-3xl bg-white p-8 shadow-xl ring-1 ring-gray-900/10 sm:p-10'
className="flex flex-col justify-between rounded-3xl bg-white p-8 shadow-xl ring-1 ring-gray-900/10 sm:p-10"
>
<div>
<h3 id={price.id} className='text-base font-semibold leading-7 text-indigo-600'>
<h3
id={price.id}
className="text-base font-semibold leading-7 text-indigo-600"
>
{price.name}
</h3>
<div className='mt-4 flex items-baseline gap-x-2'>
<span className='text-5xl font-bold tracking-tight text-gray-900'>
<div className="mt-4 flex items-baseline gap-x-2">
<span className="text-5xl font-bold tracking-tight text-gray-900">
{price.priceMonthly || price.price}
</span>
{price.priceMonthly && (
<span className='text-base font-semibold leading-7 text-gray-600'>/month</span>
<span className="text-base font-semibold leading-7 text-gray-600">
/month
</span>
)}
</div>
<p className='mt-6 text-base leading-7 text-gray-600'>{price.description}</p>
<ul role='list' className='mt-10 space-y-4 text-sm leading-6 text-gray-600'>
<p className="mt-6 text-base leading-7 text-gray-600">
{price.description}
</p>
<ul
role="list"
className="mt-10 space-y-4 text-sm leading-6 text-gray-600"
>
{price.features.map((feature) => (
<li key={feature} className='flex gap-x-3'>
<AiOutlineCheck className='h-6 w-5 flex-none text-indigo-600' aria-hidden='true' />
<li key={feature} className="flex gap-x-3">
<AiOutlineCheck
className="h-6 w-5 flex-none text-indigo-600"
aria-hidden="true"
/>
{feature}
</li>
))}
Expand All @@ -77,10 +93,11 @@ export default function PricingPage() {
aria-describedby={price.id}
disabled={price.disabled}
className={`${
price.disabled && 'disabled:opacity-25 disabled:cursor-not-allowed'
price.disabled &&
"disabled:opacity-25 disabled:cursor-not-allowed"
} mt-8 block rounded-md bg-yellow-400 px-3.5 py-2 text-center text-sm font-semibold leading-6 text-black shadow-sm hover:bg-yellow-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-yellow-600`}
>
{isLoading ? 'Loading...' : 'Buy Now'}
{isLoading ? "Loading..." : "Buy Now"}
</button>
</div>
))}
Expand Down
Loading

0 comments on commit 956689b

Please sign in to comment.