Skip to content

Commit

Permalink
feat: Add Review column to Workspace Planner and Update bountycard store
Browse files Browse the repository at this point in the history
  • Loading branch information
MuhammadUmer44 committed Jan 5, 2025
1 parent 8d8f679 commit 5284825
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 20 deletions.
1 change: 1 addition & 0 deletions src/config/colors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const palette = {
statusPending: '#ADD8E6',
statusFailed: '#FF0000',
statusPaid: '#8256D0',
statusReview: '#ff9248',
button_primary: {
main: '#49C998',
hover: '#3CBE88',
Expand Down
4 changes: 3 additions & 1 deletion src/people/WorkSpacePlanner/BountyCard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ const StatusText = styled.span<{ status?: BountyCardStatus }>`
return colors.light.statusPaid;
case 'Complete':
return colors.light.statusCompleted;
case 'Review':
return colors.light.statusReview;
case 'Assigned':
return colors.light.statusAssigned;
default:
Expand Down Expand Up @@ -164,7 +166,7 @@ BountyCardComponent.propTypes = {
workspace: PropTypes.shape({
name: PropTypes.string
}) as PropTypes.Validator<BountyCard['workspace']>,
status: PropTypes.oneOf(['Todo', 'Assigned', 'Complete', 'Paid'] as BountyCardStatus[]),
status: PropTypes.oneOf(['Todo', 'Assigned', 'Review', 'Complete', 'Paid'] as BountyCardStatus[]),
onclick: PropTypes.func.isRequired
};

Expand Down
2 changes: 2 additions & 0 deletions src/people/WorkSpacePlanner/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ const ErrorMessage = styled.p`
const COLUMN_CONFIGS = [
{ id: 'Todo', title: 'To Do' },
{ id: 'Assigned', title: 'In Progress' },
{ id: 'Review', title: 'In Review' },
{ id: 'Complete', title: 'Complete' },
{ id: 'Paid', title: 'Paid' }
];
Expand All @@ -141,6 +142,7 @@ const getCardStatus = (card: BountyCard) => {
if (!card.status) return 'Todo';
if (card.status === 'Paid') return 'Paid';
if (card.status === 'Complete') return 'Complete';
if (card.status === 'Review') return 'Review';
if (card.status === 'Assigned') return 'Assigned';
return 'Todo';
};
Expand Down
60 changes: 54 additions & 6 deletions src/store/__test__/bountyCard.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,13 +216,60 @@ describe('BountyCardStore', () => {
expect(store.bountyCards[0].status).toBe('Todo');
});

it('should return "Review" when bounty has proofs submitted', async () => {
const mockBounty = {
id: '1',
title: 'Test Bounty',
paid: false,
completed: false,
payment_pending: false,
assignee_img: 'test.jpg',
pow: 2
};

fetchStub.resolves({
ok: true,
json: async () => [mockBounty]
} as Response);

await store.loadWorkspaceBounties();
expect(store.bountyCards[0].status).toBe('Review');
});

it('should prioritize paid status over review status', async () => {
const mockBounty = {
id: '1',
title: 'Test Bounty',
paid: true,
completed: true,
payment_pending: false,
assignee_img: 'test.jpg',
pow: 2
};

fetchStub.resolves({
ok: true,
json: async () => [mockBounty]
} as Response);

await store.loadWorkspaceBounties();
expect(store.bountyCards[0].status).toBe('Paid');
});

describe('computed status lists', () => {
it('should correctly filter bounties by status', async () => {
it('should correctly filter bounties by status including review', async () => {
const mockBounties = [
{ id: '1', title: 'Bounty 1', paid: true, completed: true, assignee_img: 'test.jpg' },
{ id: '2', title: 'Bounty 2', completed: true, assignee_img: 'test.jpg' },
{ id: '3', title: 'Bounty 3', assignee_img: 'test.jpg' },
{ id: '4', title: 'Bounty 4' }
{
id: '1',
title: 'Bounty 1',
paid: true,
completed: true,
assignee_img: 'test.jpg',
pow: 0
},
{ id: '2', title: 'Bounty 2', completed: true, assignee_img: 'test.jpg', pow: 0 },
{ id: '3', title: 'Bounty 3', assignee_img: 'test.jpg', pow: 2 },
{ id: '4', title: 'Bounty 4', pow: 0 }
];

fetchStub.resolves({
Expand All @@ -234,7 +281,8 @@ describe('BountyCardStore', () => {

expect(store.paidItems.length).toBe(1);
expect(store.completedItems.length).toBe(1);
expect(store.assignedItems.length).toBe(1);
expect(store.reviewItems.length).toBe(1);
expect(store.assignedItems.length).toBe(0);
expect(store.todoItems.length).toBe(1);
});
});
Expand Down
60 changes: 48 additions & 12 deletions src/store/bountyCard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,19 @@ export class BountyCardStore {
if (bounty.completed || bounty.payment_pending) {
return 'Complete';
}
if (typeof bounty.pow === 'number' && bounty.pow > 0) {
return 'Review';
}
if (bounty.assignee_img) {
return 'Assigned';
}
return 'Todo';
}

loadWorkspaceBounties = async (): Promise<void> => {
if (!this.currentWorkspaceId || !uiStore.meInfo?.tribe_jwt) {
const jwt = uiStore.meInfo?.tribe_jwt;

if (!this.currentWorkspaceId || !jwt) {
runInAction(() => {
this.error = 'Missing workspace ID or authentication';
});
Expand All @@ -64,33 +69,59 @@ export class BountyCardStore {
});

const queryParams = this.constructQueryParams();
const response = await fetch(
`${TribesURL}/gobounties/bounty-cards?workspace_uuid=${this.currentWorkspaceId}&${queryParams}`,
{
method: 'GET',
headers: {
'x-jwt': uiStore.meInfo.tribe_jwt,
'Content-Type': 'application/json'
}
const url = `${TribesURL}/gobounties/bounty-cards?workspace_uuid=${this.currentWorkspaceId}&${queryParams}`;

const response = await fetch(url, {
method: 'GET',
headers: {
'x-jwt': jwt,
'Content-Type': 'application/json'
}
);
});

if (!response.ok) {
throw new Error(`Failed to load bounties: ${response.statusText}`);
}

const data = (await response.json()) as BountyCard[] | null;

// Fetch proof counts for each bounty
const bountyCardsWithProofs = await Promise.all((data || []).map(async (bounty: BountyCard) => {
try {
const proofsUrl = `${TribesURL}/gobounties/${bounty.id}/proofs`;
const proofsResponse = await fetch(proofsUrl, {
method: 'GET',
headers: {
'x-jwt': jwt,
'Content-Type': 'application/json'
}
});

if (!proofsResponse.ok) {
return { ...bounty, pow: 0 };
}

const proofs = await proofsResponse.json();
return {
...bounty,
pow: Array.isArray(proofs) ? proofs.length : 0
};
} catch (error) {
console.error(`Error fetching proofs for bounty ${bounty.id}:`, error);
return { ...bounty, pow: 0 };
}
}));

runInAction(() => {
if (this.pagination.currentPage === 1) {
this.bountyCards = (data || []).map((bounty: BountyCard) => ({
this.bountyCards = bountyCardsWithProofs.map((bounty: BountyCard) => ({
...bounty,
status: this.calculateBountyStatus(bounty)
}));
} else {
this.bountyCards = [
...this.bountyCards,
...(data || []).map((bounty: BountyCard) => ({
...bountyCardsWithProofs.map((bounty: BountyCard) => ({
...bounty,
status: this.calculateBountyStatus(bounty)
}))
Expand All @@ -99,6 +130,7 @@ export class BountyCardStore {
this.pagination.total = data?.length || 0;
});
} catch (error) {
console.error('Error loading bounties:', error);
runInAction(() => {
this.error = error instanceof Error ? error.message : 'An unknown error occurred';
});
Expand Down Expand Up @@ -152,6 +184,10 @@ export class BountyCardStore {
return this.bountyCards.filter((card: BountyCard) => card.status === 'Paid');
}

@computed get reviewItems() {
return this.bountyCards.filter((card: BountyCard) => card.status === 'Review');
}

@action
saveFilterState() {
sessionStorage.setItem(
Expand Down
3 changes: 2 additions & 1 deletion src/store/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ export type ChatRole = 'user' | 'assistant';
export type ChatStatus = 'sending' | 'sent' | 'error';
export type ContextTagType = 'productBrief' | 'featureBrief' | 'schematic';
export type ChatSource = 'user' | 'agent';
export type BountyCardStatus = 'Todo' | 'Assigned' | 'Complete' | 'Paid';
export type BountyCardStatus = 'Todo' | 'Assigned' | 'Review' | 'Complete' | 'Paid';

export interface ContextTag {
type: ContextTagType;
Expand Down Expand Up @@ -531,6 +531,7 @@ export interface BountyCard {
completed?: boolean;
payment_pending?: boolean;
assignee?: string;
pow?: number;
}

export type BountyReviewStatus = 'New' | 'Accepted' | 'Rejected' | 'Change Requested';
Expand Down

0 comments on commit 5284825

Please sign in to comment.