Skip to content

Commit

Permalink
Merge pull request stakwork#830 from jordan-ae/preview-switcher-bounties
Browse files Browse the repository at this point in the history
  • Loading branch information
humansinstitute authored Dec 31, 2024
2 parents 29bc4e7 + 7ac15e0 commit 1a7ce96
Show file tree
Hide file tree
Showing 2 changed files with 189 additions and 135 deletions.
86 changes: 70 additions & 16 deletions src/components/form/inputs/TextAreaInput.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React, { useState } from 'react';
import styled from 'styled-components';
import { EuiIcon } from '@elastic/eui';
import { renderMarkdown } from 'people/utils/RenderMarkdown';
import { CopyButtonGroup } from 'people/widgetViews/workspace/style';
import { colors } from '../../../config/colors';
import type { Props } from './propsType';
import { FieldEnv, FieldTextArea, Note } from './index';
Expand Down Expand Up @@ -75,6 +77,40 @@ const E = styled.div<styledProps>`
const R = styled.div`
position: relative;
`;
const SwitcherContainer = styled.div`
display: flex;
background-color: rgb(238, 231, 231);
border-radius: 20px;
padding: 4px;
width: fit-content;
margin-bottom: 12px;
align-self: flex-end;
`;

const SwitcherButton = styled.button<{ isActive: boolean }>`
padding: 8px 16px;
border: none;
border-radius: 16px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: all 0.3s ease;
${(props: { isActive: boolean }) =>
props.isActive
? `
background-color: #007bff;
color: white;
box-shadow: 0 2px 4px rgba(0, 123, 255, 0.2);
`
: `
background-color: transparent;
color: #333;
&:hover {
background-color: rgba(0, 123, 255, 0.1);
}
`}
`;
export default function TextAreaInput({
error,
note,
Expand All @@ -90,6 +126,7 @@ export default function TextAreaInput({
}: Props) {
const color = colors['light'];
const [active, setActive] = useState<boolean>(false);
const [activeMode, setActiveMode] = useState<'preview' | 'edit'>('edit');
const normalizeAndTrimText = (text: string) => text.split('\n').join('\n');

const handleTextChange = (e: any) => {
Expand All @@ -106,6 +143,19 @@ export default function TextAreaInput({

return (
<OuterContainer color={color}>
<CopyButtonGroup>
<SwitcherContainer>
<SwitcherButton
isActive={activeMode === 'preview'}
onClick={() => setActiveMode('preview')}
>
Preview
</SwitcherButton>
<SwitcherButton isActive={activeMode === 'edit'} onClick={() => setActiveMode('edit')}>
Edit
</SwitcherButton>
</SwitcherContainer>
</CopyButtonGroup>
<FieldEnv
color={color}
onClick={() => {
Expand All @@ -118,22 +168,26 @@ export default function TextAreaInput({
width={StyleOnText[label]?.width ?? defaultWidth}
>
<R>
<FieldTextArea
color={color}
height={StyleOnText[label]?.height ?? defaultHeight}
width={StyleOnText[label]?.width ?? defaultWidth}
name="first"
value={value || ''}
readOnly={readOnly || github_state || false}
onChange={handleTextChange}
onBlur={handleTextBlur}
onFocus={(e: any) => {
handleFocus(e);
setActive(true);
}}
rows={label === 'Description' ? 8 : 6}
data-testid={`checktextarea`}
/>
{activeMode === 'edit' ? (
<FieldTextArea
color={color}
height={StyleOnText[label]?.height ?? defaultHeight}
width={StyleOnText[label]?.width ?? defaultWidth}
name="first"
value={value || ''}
readOnly={readOnly || github_state || false}
onChange={handleTextChange}
onBlur={handleTextBlur}
onFocus={(e: any) => {
handleFocus(e);
setActive(true);
}}
rows={label === 'Description' ? 8 : 6}
data-testid={`checktextarea`}
/>
) : (
<div className="p-4">{renderMarkdown(value)}</div>
)}
{error && (
<E color={color}>
<EuiIcon type="alert" size="m" style={{ width: 20, height: 20 }} />
Expand Down
238 changes: 119 additions & 119 deletions src/store/bountyReviewStore.ts
Original file line number Diff line number Diff line change
@@ -1,119 +1,119 @@
import { makeAutoObservable } from 'mobx';
import { TribesURL } from 'config';
import { ProofOfWork, BountyTiming } from './interface';

export class BountyReviewStore {
proofs: Record<string, ProofOfWork[]> = {};
timings: Record<string, BountyTiming> = {};
loading = false;
error: string | null = null;

constructor() {
makeAutoObservable(this);
}

setProofs(bountyId: string, proofs: ProofOfWork[]) {
this.proofs[bountyId] = proofs;
}

setTiming(bountyId: string, timing: BountyTiming) {
this.timings[bountyId] = timing;
}

setLoading(loading: boolean) {
this.loading = loading;
}

setError(error: string | null) {
this.error = error;
}

async getProofs(bountyId: string) {
try {
this.setLoading(true);
const response = await fetch(`${TribesURL}/bounties/${bountyId}/proofs`, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
});

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

const data = (await response.json()) as ProofOfWork[];
this.setProofs(bountyId, data);
} catch (error: any) {
this.setError(error.message);
} finally {
this.setLoading(false);
}
}

async submitProof(bountyId: string, description: string) {
try {
this.setLoading(true);

// Submit proof
let response = await fetch(`${TribesURL}/bounties/${bountyId}/proof`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ description })
});

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

const proof = (await response.json()) as ProofOfWork;

// Pause timer
response = await fetch(`${TribesURL}/bounties/${bountyId}/timing/close`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
}
});

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

// Update local state
const proofs = [...(this.proofs[bountyId] || []), proof];
this.setProofs(bountyId, proofs);
} catch (error: any) {
this.setError(error.message);
} finally {
this.setLoading(false);
}
}

async getTiming(bountyId: string) {
try {
this.setLoading(true);
const response = await fetch(`${TribesURL}/bounties/${bountyId}/timing-stats`, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
});

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

const data = (await response.json()) as BountyTiming;
this.setTiming(bountyId, data);
} catch (error: any) {
this.setError(error.message);
} finally {
this.setLoading(false);
}
}
}

export const bountyReviewStore = new BountyReviewStore();
import { makeAutoObservable } from 'mobx';
import { TribesURL } from 'config';
import { ProofOfWork, BountyTiming } from './interface';

export class BountyReviewStore {
proofs: Record<string, ProofOfWork[]> = {};
timings: Record<string, BountyTiming> = {};
loading = false;
error: string | null = null;

constructor() {
makeAutoObservable(this);
}

setProofs(bountyId: string, proofs: ProofOfWork[]) {
this.proofs[bountyId] = proofs;
}

setTiming(bountyId: string, timing: BountyTiming) {
this.timings[bountyId] = timing;
}

setLoading(loading: boolean) {
this.loading = loading;
}

setError(error: string | null) {
this.error = error;
}

async getProofs(bountyId: string) {
try {
this.setLoading(true);
const response = await fetch(`${TribesURL}/bounties/${bountyId}/proofs`, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
});

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

const data = (await response.json()) as ProofOfWork[];
this.setProofs(bountyId, data);
} catch (error: any) {
this.setError(error.message);
} finally {
this.setLoading(false);
}
}

async submitProof(bountyId: string, description: string) {
try {
this.setLoading(true);

// Submit proof
let response = await fetch(`${TribesURL}/bounties/${bountyId}/proof`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ description })
});

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

const proof = (await response.json()) as ProofOfWork;

// Pause timer
response = await fetch(`${TribesURL}/bounties/${bountyId}/timing/close`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
}
});

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

// Update local state
const proofs = [...(this.proofs[bountyId] || []), proof];
this.setProofs(bountyId, proofs);
} catch (error: any) {
this.setError(error.message);
} finally {
this.setLoading(false);
}
}

async getTiming(bountyId: string) {
try {
this.setLoading(true);
const response = await fetch(`${TribesURL}/bounties/${bountyId}/timing-stats`, {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
});

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

const data = (await response.json()) as BountyTiming;
this.setTiming(bountyId, data);
} catch (error: any) {
this.setError(error.message);
} finally {
this.setLoading(false);
}
}
}

export const bountyReviewStore = new BountyReviewStore();

0 comments on commit 1a7ce96

Please sign in to comment.