diff --git a/src/pages/superadmin/header/FeatureBountyModal.tsx b/src/pages/superadmin/header/FeatureBountyModal.tsx
new file mode 100644
index 00000000..2173e1e3
--- /dev/null
+++ b/src/pages/superadmin/header/FeatureBountyModal.tsx
@@ -0,0 +1,130 @@
+import React, { useState } from 'react';
+import { nonWidgetConfigs } from 'people/utils/Constants';
+import { useIsMobile } from 'hooks/uiHooks';
+import { InvoiceForm, InvoiceInput, InvoiceLabel } from 'people/utils/style';
+import styled from 'styled-components';
+import { BudgetButton } from 'people/widgetViews/workspace/style';
+import { Modal } from '../../../components/common';
+import { bountyStore } from '../../../store/bountyStore';
+
+interface FeatureBountyProps {
+ open: boolean;
+ close: () => void;
+ addToast?: (title: string, color: 'success' | 'error') => void;
+}
+
+const ModalTitle = styled.h3`
+ font-size: 1.9rem;
+ font-weight: bolder;
+ margin-bottom: 20px;
+`;
+
+const Wrapper = styled.div`
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ padding: 40px 50px;
+`;
+
+const FeatureBountyModal = (props: FeatureBountyProps) => {
+ const isMobile = useIsMobile();
+ const { open, close, addToast } = props;
+ const [loading, setLoading] = useState(false);
+ const [bountyUrl, setBountyUrl] = useState('');
+ const config = nonWidgetConfigs['workspaceusers'];
+
+ const handleAddFeaturedBounty = async () => {
+ setLoading(true);
+ try {
+ const bountyId = bountyStore.getBountyIdFromURL(bountyUrl);
+
+ if (!bountyId) {
+ if (addToast) addToast('Invalid bounty URL format', 'error');
+ return;
+ }
+
+ if (bountyStore.hasBounty(bountyId)) {
+ if (addToast) addToast('Bounty already in featured list', 'error');
+ return;
+ }
+
+ bountyStore.addFeaturedBounty(bountyId);
+
+ if (addToast) addToast('Bounty added to featured list', 'success');
+ setBountyUrl('');
+ close();
+ } catch (error) {
+ if (addToast) addToast('Could not add bounty to featured list', 'error');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ return (
+ <>
+
+
+ Featured Bounties
+
+
+ URL of the feature bounty
+
+ ) => setBountyUrl(e.target.value)}
+ placeholder="Enter bounty URL"
+ />
+
+
+ Confirm
+
+
+
+ >
+ );
+};
+
+export default FeatureBountyModal;
diff --git a/src/pages/superadmin/header/HeaderStyles.tsx b/src/pages/superadmin/header/HeaderStyles.tsx
index ff9d9e69..d1d952b2 100644
--- a/src/pages/superadmin/header/HeaderStyles.tsx
+++ b/src/pages/superadmin/header/HeaderStyles.tsx
@@ -87,6 +87,22 @@ export const ExportButton = styled.button`
box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.06);
margin-right: 10px;
`;
+
+export const FeaturedButton = styled.button`
+ width: auto;
+ padding: 8px 16px;
+ height: 40px;
+ justify-content: center;
+ align-items: center;
+ gap: 6px;
+ border-radius: 6px;
+ border: 1px solid var(--Input-Outline-1, #d0d5d8);
+ background: var(--White, #fff);
+ box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.06);
+ margin-right: 10px;
+ margin-top: 1px;
+`;
+
export const ExportText = styled.p`
color: var(--Main-bottom-icons, #5f6368);
text-align: center;
diff --git a/src/pages/superadmin/header/index.tsx b/src/pages/superadmin/header/index.tsx
index 5d2baed8..8fdbef25 100644
--- a/src/pages/superadmin/header/index.tsx
+++ b/src/pages/superadmin/header/index.tsx
@@ -16,13 +16,15 @@ import {
Option,
CustomButton,
WorkspaceOption,
- WorkspaceText
+ WorkspaceText,
+ FeaturedButton
} from './HeaderStyles';
import arrowback from './icons/arrowback.svg';
import arrowforward from './icons/arrowforward.svg';
import expand_more from './icons/expand_more.svg';
import App from './components/Calendar/App';
import InviteModal from './InviteModal';
+import FeatureBountyModal from './FeatureBountyModal';
interface HeaderProps {
startDate?: number;
@@ -51,6 +53,8 @@ export const Header = ({
const [workspaceText, setWorkspaceText] = useState('Workspaces ...');
const [workspaces, setWorkspaces] = useState([]);
const [openInvite, setOpenInvite] = useState(false);
+ const [openFeatureBounty, setOpenFeatureBounty] = useState(false);
+
const formatUnixDate = (unixDate: number) => {
const formatString = 'DD MMM YYYY';
if (startDate !== undefined && endDate !== undefined) {
@@ -85,6 +89,10 @@ export const Header = ({
}
};
+ const toggleFeatureBountyModal = () => {
+ setOpenFeatureBounty(!openFeatureBounty);
+ };
+
const toggleModal = () => {
setOpenInvite(!openInvite);
};
@@ -208,6 +216,9 @@ export const Header = ({
) : null}
+ toggleFeatureBountyModal()}>
+ Featured Bounty
+
toggleModal()}>
Invite Users
@@ -274,6 +285,11 @@ export const Header = ({
/>
)}
+
);
};
diff --git a/src/store/bountyStore.ts b/src/store/bountyStore.ts
new file mode 100644
index 00000000..7ee08342
--- /dev/null
+++ b/src/store/bountyStore.ts
@@ -0,0 +1,88 @@
+import { makeAutoObservable } from 'mobx';
+
+interface FeaturedBounty {
+ bountyId: string;
+ sequence: number;
+}
+
+class BountyStore {
+ featuredBounties: FeaturedBounty[] = [];
+
+ constructor() {
+ makeAutoObservable(this);
+ this.loadFromStorage();
+ }
+
+ private loadFromStorage(): void {
+ try {
+ const saved = localStorage.getItem('featuredBounties');
+ if (saved) {
+ this.featuredBounties = JSON.parse(saved);
+ }
+ } catch (error) {
+ console.error('Error loading from storage:', error);
+ }
+ }
+
+ private saveToStorage(): void {
+ try {
+ localStorage.setItem('featuredBounties', JSON.stringify(this.featuredBounties));
+ } catch (error) {
+ console.error('Error saving to storage:', error);
+ }
+ }
+
+ getBountyIdFromURL(url: string): string | null {
+ const match = url.match(/\/bounty\/(\d+)$/);
+ return match ? match[1] : null;
+ }
+
+ addFeaturedBounty(bountyId: string): void {
+ const nextSequence = this.featuredBounties.length + 1;
+ const exists = this.featuredBounties.some((b: FeaturedBounty) => b.bountyId === bountyId);
+
+ if (!exists) {
+ this.featuredBounties.push({
+ bountyId,
+ sequence: nextSequence
+ });
+ this.saveToStorage();
+ }
+ }
+
+ removeFeaturedBounty(bountyId: string): void {
+ this.featuredBounties = this.featuredBounties.filter(
+ (b: FeaturedBounty) => b.bountyId !== bountyId
+ );
+ this.featuredBounties.forEach((bounty: FeaturedBounty, index: number) => {
+ bounty.sequence = index + 1;
+ });
+ this.saveToStorage();
+ }
+
+ getFeaturedBounties(): FeaturedBounty[] {
+ return this.featuredBounties
+ .slice()
+ .sort((a: FeaturedBounty, b: FeaturedBounty) => a.sequence - b.sequence);
+ }
+
+ updateSequence(bountyId: string, newSequence: number): void {
+ const bounty = this.featuredBounties.find((b: FeaturedBounty) => b.bountyId === bountyId);
+ if (bounty) {
+ bounty.sequence = newSequence;
+ this.saveToStorage();
+ }
+ }
+
+ hasBounty(bountyId: string): boolean {
+ const exists = this.featuredBounties.some((b: FeaturedBounty) => b.bountyId === bountyId);
+ return exists;
+ }
+
+ clearAllBounties(): void {
+ this.featuredBounties = [];
+ this.saveToStorage();
+ }
+}
+
+export const bountyStore = new BountyStore();