mirror of
https://github.com/aykhans/slash-e.git
synced 2025-04-19 05:25:43 +00:00
chore: fix feature checks
This commit is contained in:
parent
a5bc443db9
commit
c98e717f5b
@ -4,7 +4,7 @@ import { Outlet } from "react-router-dom";
|
|||||||
import DemoBanner from "@/components/DemoBanner";
|
import DemoBanner from "@/components/DemoBanner";
|
||||||
import { useWorkspaceStore } from "@/stores";
|
import { useWorkspaceStore } from "@/stores";
|
||||||
import useNavigateTo from "./hooks/useNavigateTo";
|
import useNavigateTo from "./hooks/useNavigateTo";
|
||||||
import { PlanType } from "./types/proto/api/v1/subscription_service";
|
import { FeatureType } from "./stores/workspace";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const navigateTo = useNavigateTo();
|
const navigateTo = useNavigateTo();
|
||||||
@ -28,7 +28,7 @@ function App() {
|
|||||||
}, [workspaceStore.setting.customStyle]);
|
}, [workspaceStore.setting.customStyle]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const hasCustomBranding = workspaceStore.profile.subscription?.plan === PlanType.PRO;
|
const hasCustomBranding = workspaceStore.checkFeatureAvailable(FeatureType.CustomeBranding);
|
||||||
if (!hasCustomBranding || !workspaceStore.setting.branding) {
|
if (!hasCustomBranding || !workspaceStore.setting.branding) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ const Header: React.FC = () => {
|
|||||||
const workspaceStore = useWorkspaceStore();
|
const workspaceStore = useWorkspaceStore();
|
||||||
const currentUser = useUserStore().getCurrentUser();
|
const currentUser = useUserStore().getCurrentUser();
|
||||||
const [showAboutDialog, setShowAboutDialog] = useState<boolean>(false);
|
const [showAboutDialog, setShowAboutDialog] = useState<boolean>(false);
|
||||||
const profile = workspaceStore.profile;
|
const subscription = workspaceStore.getSubscription();
|
||||||
const isAdmin = currentUser.role === Role.ADMIN;
|
const isAdmin = currentUser.role === Role.ADMIN;
|
||||||
const shouldShowRouterSwitch = location.pathname === "/shortcuts" || location.pathname === "/collections";
|
const shouldShowRouterSwitch = location.pathname === "/shortcuts" || location.pathname === "/collections";
|
||||||
const selectedSection = location.pathname === "/shortcuts" ? "Shortcuts" : location.pathname === "/collections" ? "Collections" : "";
|
const selectedSection = location.pathname === "/shortcuts" ? "Shortcuts" : location.pathname === "/collections" ? "Collections" : "";
|
||||||
@ -35,10 +35,10 @@ const Header: React.FC = () => {
|
|||||||
<Logo className="mr-2" />
|
<Logo className="mr-2" />
|
||||||
Slash
|
Slash
|
||||||
</Link>
|
</Link>
|
||||||
{profile.subscription?.plan && [PlanType.PRO, PlanType.ENTERPRISE].includes(profile.subscription.plan) && (
|
{[PlanType.PRO, PlanType.ENTERPRISE].includes(subscription.plan) && (
|
||||||
<span className="ml-1 text-xs px-1.5 leading-5 border rounded-full bg-blue-600 border-blue-700 text-white shadow dark:opacity-70">
|
<span className="ml-1 text-xs px-1.5 leading-5 border rounded-full bg-blue-600 border-blue-700 text-white shadow dark:opacity-70">
|
||||||
{/* PRO or ENT */}
|
{/* PRO or ENT */}
|
||||||
{profile.subscription.plan.substring(0, 3)}
|
{subscription.plan.substring(0, 3)}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{shouldShowRouterSwitch && (
|
{shouldShowRouterSwitch && (
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { useWorkspaceStore } from "@/stores";
|
import { useWorkspaceStore } from "@/stores";
|
||||||
import { PlanType } from "@/types/proto/api/v1/subscription_service";
|
import { FeatureType } from "@/stores/workspace";
|
||||||
import Icon from "./Icon";
|
import Icon from "./Icon";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -9,7 +9,7 @@ interface Props {
|
|||||||
|
|
||||||
const Logo = ({ className }: Props) => {
|
const Logo = ({ className }: Props) => {
|
||||||
const workspaceStore = useWorkspaceStore();
|
const workspaceStore = useWorkspaceStore();
|
||||||
const hasCustomBranding = workspaceStore.profile.subscription?.plan === PlanType.PRO;
|
const hasCustomBranding = workspaceStore.checkFeatureAvailable(FeatureType.CustomeBranding);
|
||||||
const branding = hasCustomBranding && workspaceStore.setting.branding ? new TextDecoder().decode(workspaceStore.setting.branding) : "";
|
const branding = hasCustomBranding && workspaceStore.setting.branding ? new TextDecoder().decode(workspaceStore.setting.branding) : "";
|
||||||
return (
|
return (
|
||||||
<div className={classNames("w-8 h-auto dark:text-gray-500 rounded-lg overflow-hidden", className)}>
|
<div className={classNames("w-8 h-auto dark:text-gray-500 rounded-lg overflow-hidden", className)}>
|
||||||
|
@ -7,7 +7,6 @@ import { workspaceServiceClient } from "@/grpcweb";
|
|||||||
import { useWorkspaceStore } from "@/stores";
|
import { useWorkspaceStore } from "@/stores";
|
||||||
import { FeatureType } from "@/stores/workspace";
|
import { FeatureType } from "@/stores/workspace";
|
||||||
import { Visibility } from "@/types/proto/api/v1/common";
|
import { Visibility } from "@/types/proto/api/v1/common";
|
||||||
import { PlanType } from "@/types/proto/api/v1/subscription_service";
|
|
||||||
import { WorkspaceSetting } from "@/types/proto/api/v1/workspace_service";
|
import { WorkspaceSetting } from "@/types/proto/api/v1/workspace_service";
|
||||||
import FeatureBadge from "../FeatureBadge";
|
import FeatureBadge from "../FeatureBadge";
|
||||||
import Icon from "../Icon";
|
import Icon from "../Icon";
|
||||||
@ -35,7 +34,7 @@ const WorkspaceSection = () => {
|
|||||||
const [workspaceSetting, setWorkspaceSetting] = useState<WorkspaceSetting>(workspaceStore.setting);
|
const [workspaceSetting, setWorkspaceSetting] = useState<WorkspaceSetting>(workspaceStore.setting);
|
||||||
const originalWorkspaceSetting = useRef<WorkspaceSetting>(workspaceStore.setting);
|
const originalWorkspaceSetting = useRef<WorkspaceSetting>(workspaceStore.setting);
|
||||||
const allowSave = !isEqual(originalWorkspaceSetting.current, workspaceSetting);
|
const allowSave = !isEqual(originalWorkspaceSetting.current, workspaceSetting);
|
||||||
const hasCustomBranding = workspaceStore.profile.subscription?.plan === PlanType.PRO;
|
const hasCustomBranding = workspaceStore.checkFeatureAvailable(FeatureType.CustomeBranding);
|
||||||
const branding = hasCustomBranding && workspaceSetting.branding ? new TextDecoder().decode(workspaceSetting.branding) : "";
|
const branding = hasCustomBranding && workspaceSetting.branding ? new TextDecoder().decode(workspaceSetting.branding) : "";
|
||||||
|
|
||||||
const onBrandingChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
const onBrandingChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
@ -14,7 +14,7 @@ const SubscriptionSetting: React.FC = () => {
|
|||||||
const currentUser = useUserStore().getCurrentUser();
|
const currentUser = useUserStore().getCurrentUser();
|
||||||
const [licenseKey, setLicenseKey] = useState<string>("");
|
const [licenseKey, setLicenseKey] = useState<string>("");
|
||||||
const isAdmin = currentUser.role === Role.ADMIN;
|
const isAdmin = currentUser.role === Role.ADMIN;
|
||||||
const profile = workspaceStore.profile;
|
const subscription = workspaceStore.getSubscription();
|
||||||
|
|
||||||
const handleDeleteLicenseKey = async () => {
|
const handleDeleteLicenseKey = async () => {
|
||||||
if (!isAdmin) {
|
if (!isAdmin) {
|
||||||
@ -58,7 +58,7 @@ const SubscriptionSetting: React.FC = () => {
|
|||||||
<p className="text-2xl shrink-0 font-semibold text-gray-900 dark:text-gray-500">Subscription</p>
|
<p className="text-2xl shrink-0 font-semibold text-gray-900 dark:text-gray-500">Subscription</p>
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
<span className="text-gray-500 mr-2">Current plan:</span>
|
<span className="text-gray-500 mr-2">Current plan:</span>
|
||||||
<span className="text-2xl mr-4 dark:text-gray-400">{stringifyPlanType(profile.subscription?.plan)}</span>
|
<span className="text-2xl mr-4 dark:text-gray-400">{stringifyPlanType(subscription.plan)}</span>
|
||||||
</div>
|
</div>
|
||||||
<Textarea
|
<Textarea
|
||||||
className="w-full mt-2"
|
className="w-full mt-2"
|
||||||
@ -70,7 +70,7 @@ const SubscriptionSetting: React.FC = () => {
|
|||||||
/>
|
/>
|
||||||
<div className="w-full flex justify-between items-center mt-4">
|
<div className="w-full flex justify-between items-center mt-4">
|
||||||
<div>
|
<div>
|
||||||
{profile.subscription?.plan === PlanType.FREE && (
|
{subscription.plan === PlanType.FREE && (
|
||||||
<Link href="https://yourselfhosted.lemonsqueezy.com/checkout/buy/947e9a56-c93a-4294-8d71-2ea4b0f3ec51" target="_blank">
|
<Link href="https://yourselfhosted.lemonsqueezy.com/checkout/buy/947e9a56-c93a-4294-8d71-2ea4b0f3ec51" target="_blank">
|
||||||
Buy a license key
|
Buy a license key
|
||||||
<Icon.ExternalLink className="w-4 h-auto ml-1" />
|
<Icon.ExternalLink className="w-4 h-auto ml-1" />
|
||||||
@ -78,7 +78,7 @@ const SubscriptionSetting: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex justify-end items-center gap-2">
|
<div className="flex justify-end items-center gap-2">
|
||||||
{profile.subscription?.plan !== PlanType.FREE && (
|
{subscription.plan !== PlanType.FREE && (
|
||||||
<Button color="neutral" variant="plain" onClick={handleDeleteLicenseKey}>
|
<Button color="neutral" variant="plain" onClick={handleDeleteLicenseKey}>
|
||||||
Reset
|
Reset
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -12,7 +12,6 @@ const WorkspaceSetting = () => {
|
|||||||
const workspaceStore = useWorkspaceStore();
|
const workspaceStore = useWorkspaceStore();
|
||||||
const currentUser = useUserStore().getCurrentUser();
|
const currentUser = useUserStore().getCurrentUser();
|
||||||
const isAdmin = currentUser.role === Role.ADMIN;
|
const isAdmin = currentUser.role === Role.ADMIN;
|
||||||
const profile = workspaceStore.profile;
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isAdmin) {
|
if (!isAdmin) {
|
||||||
@ -33,7 +32,7 @@ const WorkspaceSetting = () => {
|
|||||||
<p className="text-2xl shrink-0 font-semibold text-gray-900 dark:text-gray-500">Subscription</p>
|
<p className="text-2xl shrink-0 font-semibold text-gray-900 dark:text-gray-500">Subscription</p>
|
||||||
<div className="mt-2">
|
<div className="mt-2">
|
||||||
<span className="text-gray-500 mr-2">Current plan:</span>
|
<span className="text-gray-500 mr-2">Current plan:</span>
|
||||||
<span className="text-2xl mr-4 dark:text-gray-400">{stringifyPlanType(profile.subscription?.plan)}</span>
|
<span className="text-2xl mr-4 dark:text-gray-400">{stringifyPlanType(workspaceStore.getSubscription().plan)}</span>
|
||||||
<Link to="/setting/subscription" unstable_viewTransition>
|
<Link to="/setting/subscription" unstable_viewTransition>
|
||||||
<Button size="sm" variant="outlined" startDecorator={<Icon.Settings className="w-4 h-auto" />}>
|
<Button size="sm" variant="outlined" startDecorator={<Icon.Settings className="w-4 h-auto" />}>
|
||||||
Manage
|
Manage
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { workspaceServiceClient } from "@/grpcweb";
|
import { workspaceServiceClient } from "@/grpcweb";
|
||||||
|
import { Subscription } from "@/types/proto/api/v1/subscription_service";
|
||||||
import { WorkspaceProfile, WorkspaceSetting } from "@/types/proto/api/v1/workspace_service";
|
import { WorkspaceProfile, WorkspaceSetting } from "@/types/proto/api/v1/workspace_service";
|
||||||
|
|
||||||
export enum FeatureType {
|
export enum FeatureType {
|
||||||
@ -17,6 +18,7 @@ interface WorkspaceState {
|
|||||||
// Workspace related actions.
|
// Workspace related actions.
|
||||||
fetchWorkspaceProfile: () => Promise<WorkspaceProfile>;
|
fetchWorkspaceProfile: () => Promise<WorkspaceProfile>;
|
||||||
fetchWorkspaceSetting: () => Promise<WorkspaceSetting>;
|
fetchWorkspaceSetting: () => Promise<WorkspaceSetting>;
|
||||||
|
getSubscription: () => Subscription;
|
||||||
checkFeatureAvailable: (feature: FeatureType) => boolean;
|
checkFeatureAvailable: (feature: FeatureType) => boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,6 +35,7 @@ const useWorkspaceStore = create<WorkspaceState>()((set, get) => ({
|
|||||||
set({ ...get(), setting: workspaceSetting });
|
set({ ...get(), setting: workspaceSetting });
|
||||||
return workspaceSetting;
|
return workspaceSetting;
|
||||||
},
|
},
|
||||||
|
getSubscription: () => Subscription.fromPartial(get().profile.subscription || {}),
|
||||||
checkFeatureAvailable: (feature: FeatureType): boolean => {
|
checkFeatureAvailable: (feature: FeatureType): boolean => {
|
||||||
return get().profile.subscription?.features.includes(feature) || false;
|
return get().profile.subscription?.features.includes(feature) || false;
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user