diff --git a/.vscode/launch.json b/.vscode/launch.json index 7a9dfa04..e93ed3bc 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,15 +1,39 @@ { - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "pwa-chrome", - "request": "launch", - "name": "Launch Chrome against localhost", - "url": "http://localhost:8080", - "webRoot": "${workspaceFolder}" - } - ] + "version": "0.2.0", + "configurations": [ + { + "name": "Next.js: Server", + "type": "node-terminal", + "request": "launch", + "command": "npm run dev", + "skipFiles": ["/**"], + "outFiles": [ + "${workspaceFolder}/**/*.js", + "!**/node_modules/**" + ], + "sourceMaps": true, + "resolveSourceMapLocations": [ + "${workspaceFolder}/**", + "!**/node_modules/**" + ] + }, + { + "name": "Next.js: Client", + "type": "pwa-chrome", + "request": "launch", + "url": "http://localhost:3000" + }, + { + "name": "Next.js: Full Stack", + "type": "node-terminal", + "request": "launch", + "command": "npm run dev", + "console": "integratedTerminal", + "serverReadyAction": { + "pattern": "started server on .+, url: (https?://.+)", + "uriFormat": "%s", + "action": "debugWithChrome" + } + } + ] } \ No newline at end of file diff --git a/components/Dialog.tsx b/components/Dialog.tsx index e2b0d1ea..38639bb9 100644 --- a/components/Dialog.tsx +++ b/components/Dialog.tsx @@ -32,7 +32,7 @@ type DialogHeaderProps = { export function DialogHeader(props: DialogHeaderProps) { return (
- {props.subtitle &&
{props.subtitle}
} diff --git a/components/eventtype/EventTypeDescription.tsx b/components/eventtype/EventTypeDescription.tsx index 73f12a65..16f2a374 100644 --- a/components/eventtype/EventTypeDescription.tsx +++ b/components/eventtype/EventTypeDescription.tsx @@ -37,7 +37,7 @@ export const EventTypeDescription = ({ eventType, className }: EventTypeDescript {eventType.description.length > 100 && "..."} )} -
-
-
- - -
-
-
{errorMessage && (

diff --git a/components/team/MemberListItem.tsx b/components/team/MemberListItem.tsx index 0bb67d68..db809023 100644 --- a/components/team/MemberListItem.tsx +++ b/components/team/MemberListItem.tsx @@ -24,7 +24,7 @@ import Dropdown, { DropdownMenuTrigger, } from "../ui/Dropdown"; import MemberChangeRoleModal from "./MemberChangeRoleModal"; -import TeamRole from "./TeamRole"; +import TeamPill, { TeamRole } from "./TeamPill"; import { MembershipRole } from ".prisma/client"; interface Props { @@ -80,8 +80,14 @@ export default function MemberListItem(props: Props) {

- {!props.member.accepted && } - + {/* Tooltip doesn't show... WHY????? */} + {props.member.isMissingSeat && ( + + + + )} + {!props.member.accepted && } + {props.member.role && }
@@ -96,7 +102,7 @@ export default function MemberListItem(props: Props) { disabled={!props.member.accepted} onClick={() => (props.member.accepted ? setShowTeamAvailabilityModal(true) : null)} color="minimal" - className="hidden w-10 h-10 p-0 border border-transparent group text-neutral-400 hover:border-gray-200 hover:bg-white sm:block"> + className="items-center justify-center hidden w-10 h-10 px-0 py-0 border border-transparent group text-neutral-400 hover:border-gray-200 hover:bg-white sm:flex"> @@ -167,7 +173,7 @@ export default function MemberListItem(props: Props) { {showTeamAvailabilityModal && ( -
+
{props.team.membership.role !== MembershipRole.MEMBER && ( diff --git a/components/team/TeamCreateModal.tsx b/components/team/TeamCreateModal.tsx index 30f76268..fd7efb6d 100644 --- a/components/team/TeamCreateModal.tsx +++ b/components/team/TeamCreateModal.tsx @@ -1,9 +1,11 @@ import { UsersIcon } from "@heroicons/react/outline"; -import { useRef } from "react"; +import { useRef, useState } from "react"; import { useLocale } from "@lib/hooks/useLocale"; import { trpc } from "@lib/trpc"; +import { Alert } from "@components/ui/Alert"; + interface Props { onClose: () => void; } @@ -11,7 +13,7 @@ interface Props { export default function TeamCreate(props: Props) { const { t } = useLocale(); const utils = trpc.useContext(); - + const [errorMessage, setErrorMessage] = useState(null); const nameRef = useRef() as React.MutableRefObject; const createTeamMutation = trpc.useMutation("viewer.teams.create", { @@ -19,6 +21,9 @@ export default function TeamCreate(props: Props) { utils.invalidateQueries(["viewer.teams.list"]); props.onClose(); }, + onError: (e) => { + setErrorMessage(e?.message || t("something_went_wrong")); + }, }); const createTeam = (e: React.FormEvent) => { @@ -70,6 +75,7 @@ export default function TeamCreate(props: Props) { className="block w-full px-3 py-2 mt-1 border border-gray-300 rounded-sm shadow-sm focus:outline-none focus:ring-neutral-500 focus:border-neutral-500 sm:text-sm" />
+ {errorMessage && }
+ ); +} + +export function TeamRole(props: { role: MembershipRole }) { + const { t } = useLocale(); + const keys: Record = { + [MembershipRole.OWNER]: undefined, + [MembershipRole.ADMIN]: "red", + [MembershipRole.MEMBER]: "blue", + }; + return ; +} diff --git a/components/team/TeamRole.tsx b/components/team/TeamRole.tsx deleted file mode 100644 index dc4e28fb..00000000 --- a/components/team/TeamRole.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { MembershipRole } from "@prisma/client"; -import classNames from "classnames"; - -import { useLocale } from "@lib/hooks/useLocale"; - -interface Props { - role?: MembershipRole; - invitePending?: boolean; -} - -export default function TeamRole(props: Props) { - const { t } = useLocale(); - - return ( - - {(() => { - if (props.invitePending) return t("invitee"); - switch (props.role) { - case "OWNER": - return t("owner"); - case "ADMIN": - return t("admin"); - case "MEMBER": - return t("member"); - default: - return ""; - } - })()} - - ); -} diff --git a/components/team/TeamSettingsRightSidebar.tsx b/components/team/TeamSettingsRightSidebar.tsx index f2635c2c..6441c0cc 100644 --- a/components/team/TeamSettingsRightSidebar.tsx +++ b/components/team/TeamSettingsRightSidebar.tsx @@ -15,8 +15,6 @@ import LinkIconButton from "@components/ui/LinkIconButton"; import { MembershipRole } from ".prisma/client"; -// import Switch from "@components/ui/Switch"; - export default function TeamSettingsRightSidebar(props: { team: TeamWithMembers; role: MembershipRole }) { const { t } = useLocale(); const utils = trpc.useContext(); @@ -27,6 +25,7 @@ export default function TeamSettingsRightSidebar(props: { team: TeamWithMembers; const deleteTeamMutation = trpc.useMutation("viewer.teams.delete", { async onSuccess() { await utils.invalidateQueries(["viewer.teams.get"]); + router.push(`/settings/teams`); showToast(t("your_team_updated_successfully"), "success"); }, }); @@ -50,19 +49,20 @@ export default function TeamSettingsRightSidebar(props: { team: TeamWithMembers; return (
- - {/* */} + {(props.role === MembershipRole.OWNER || props.role === MembershipRole.ADMIN) && ( + + )}
diff --git a/components/team/UpgradeToFlexibleProModal.tsx b/components/team/UpgradeToFlexibleProModal.tsx new file mode 100644 index 00000000..082522ad --- /dev/null +++ b/components/team/UpgradeToFlexibleProModal.tsx @@ -0,0 +1,90 @@ +import { useState } from "react"; + +import { useLocale } from "@lib/hooks/useLocale"; +import showToast from "@lib/notification"; +import { trpc } from "@lib/trpc"; + +import { + Dialog, + DialogTrigger, + DialogContent, + DialogClose, + DialogFooter, + DialogHeader, +} from "@components/Dialog"; +import { Alert } from "@components/ui/Alert"; +import Button from "@components/ui/Button"; + +interface Props { + teamId: number; +} + +export function UpgradeToFlexibleProModal(props: Props) { + const { t } = useLocale(); + const [errorMessage, setErrorMessage] = useState(null); + const utils = trpc.useContext(); + const { data } = trpc.useQuery(["viewer.teams.getTeamSeats", { teamId: props.teamId }], { + onError: (err) => { + setErrorMessage(err.message); + }, + }); + const mutation = trpc.useMutation(["viewer.teams.upgradeTeam"], { + onSuccess: (data) => { + // if the user does not already have a Stripe subscription, this wi + if (data?.url) { + window.location.href = data.url; + } + if (data?.success) { + utils.invalidateQueries(["viewer.teams.get"]); + showToast(t("team_upgraded_successfully"), "success"); + } + }, + onError: (err) => { + setErrorMessage(err.message); + }, + }); + + return ( + { + setErrorMessage(null); + }}> + + {"Upgrade Now"} + + + + +

{t("changed_team_billing_info")}

+ {data && ( +

+ {t("team_upgrade_seats_details", { + memberCount: data.totalMembers, + unpaidCount: data.missingSeats, + seatPrice: 12, + totalCost: (data.totalMembers - data.freeSeats) * 12 + 12, + })} +

+ )} + + {errorMessage && ( + + )} + + + + + + + +
+ + ); +} diff --git a/components/ui/Alert.tsx b/components/ui/Alert.tsx index f717ccc0..430afd08 100644 --- a/components/ui/Alert.tsx +++ b/components/ui/Alert.tsx @@ -15,10 +15,10 @@ export function Alert(props: AlertProps) { return (
diff --git a/components/ui/LinkIconButton.tsx b/components/ui/LinkIconButton.tsx index eefdb0e9..c155ab36 100644 --- a/components/ui/LinkIconButton.tsx +++ b/components/ui/LinkIconButton.tsx @@ -10,8 +10,8 @@ export default function LinkIconButton(props: LinkIconButtonProps) { return (