From beae21b0c071e816909a79589e6979fdf43f329b Mon Sep 17 00:00:00 2001 From: jordan-ae Date: Mon, 30 Dec 2024 23:57:48 +0100 Subject: [PATCH 1/2] feat: added switcher to bounty creation and edit flow --- src/components/form/inputs/TextAreaInput.tsx | 86 ++++++++++++++++---- src/store/bountyReviewStore.ts | 78 +++++++++--------- 2 files changed, 109 insertions(+), 55 deletions(-) diff --git a/src/components/form/inputs/TextAreaInput.tsx b/src/components/form/inputs/TextAreaInput.tsx index 4e1727c0..57c3225c 100644 --- a/src/components/form/inputs/TextAreaInput.tsx +++ b/src/components/form/inputs/TextAreaInput.tsx @@ -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'; @@ -75,6 +77,40 @@ const E = styled.div` 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, @@ -90,6 +126,7 @@ export default function TextAreaInput({ }: Props) { const color = colors['light']; const [active, setActive] = useState(false); + const [activeMode, setActiveMode] = useState<'preview' | 'edit'>('edit'); const normalizeAndTrimText = (text: string) => text.split('\n').join('\n'); const handleTextChange = (e: any) => { @@ -106,6 +143,19 @@ export default function TextAreaInput({ return ( + + + setActiveMode('preview')} + > + Preview + + setActiveMode('edit')}> + Edit + + + { @@ -118,22 +168,26 @@ export default function TextAreaInput({ width={StyleOnText[label]?.width ?? defaultWidth} > - { - handleFocus(e); - setActive(true); - }} - rows={label === 'Description' ? 8 : 6} - data-testid={`checktextarea`} - /> + {activeMode === 'edit' ? ( + { + handleFocus(e); + setActive(true); + }} + rows={label === 'Description' ? 8 : 6} + data-testid={`checktextarea`} + /> + ) : ( +
{renderMarkdown(value)}
+ )} {error && ( diff --git a/src/store/bountyReviewStore.ts b/src/store/bountyReviewStore.ts index e5e007a0..ffb1fcab 100644 --- a/src/store/bountyReviewStore.ts +++ b/src/store/bountyReviewStore.ts @@ -1,39 +1,39 @@ -import { makeAutoObservable } from 'mobx'; -import { ProofOfWork, BountyTiming } from './interface'; - -export class BountyReviewStore { - proofs: Record = {}; - timings: Record = {}; - 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; - } - - getProofs(bountyId: string): ProofOfWork[] { - return this.proofs[bountyId] || []; - } - - getTiming(bountyId: string): BountyTiming | undefined { - return this.timings[bountyId]; - } -} - -export const bountyReviewStore = new BountyReviewStore(); +import { makeAutoObservable } from 'mobx'; +import { ProofOfWork, BountyTiming } from './interface'; + +export class BountyReviewStore { + proofs: Record = {}; + timings: Record = {}; + 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; + } + + getProofs(bountyId: string): ProofOfWork[] { + return this.proofs[bountyId] || []; + } + + getTiming(bountyId: string): BountyTiming | undefined { + return this.timings[bountyId]; + } +} + +export const bountyReviewStore = new BountyReviewStore(); From 7ac15e0037832895d5dbfd7531fa8bbec0277d70 Mon Sep 17 00:00:00 2001 From: jordan-ae Date: Tue, 31 Dec 2024 02:59:14 +0100 Subject: [PATCH 2/2] chore: fix prettier errors --- src/store/bountyReviewStore.ts | 238 ++++++++++++++++----------------- 1 file changed, 119 insertions(+), 119 deletions(-) diff --git a/src/store/bountyReviewStore.ts b/src/store/bountyReviewStore.ts index 13f5d480..6874b7cb 100644 --- a/src/store/bountyReviewStore.ts +++ b/src/store/bountyReviewStore.ts @@ -1,119 +1,119 @@ -import { makeAutoObservable } from 'mobx'; -import { TribesURL } from 'config'; -import { ProofOfWork, BountyTiming } from './interface'; - -export class BountyReviewStore { - proofs: Record = {}; - timings: Record = {}; - 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 = {}; + timings: Record = {}; + 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();