Skip to content

Commit

Permalink
Merge branch 'stakwork:master' into implement-filter-on-people-page
Browse files Browse the repository at this point in the history
  • Loading branch information
Ekep-Obasi authored Dec 9, 2023
2 parents 430dae7 + fb76cc1 commit 7659c6b
Show file tree
Hide file tree
Showing 27 changed files with 589 additions and 268 deletions.
10 changes: 10 additions & 0 deletions .github/workflows/prjob_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,13 @@ jobs:
- name: Tests
run: NODE_OPTIONS="--max_old_space_size=8192" yarn --cwd ./frontend/app run test-jest

test-go:
name: Go
runs-on:
- ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install cover
run: go get golang.org/x/tools/cmd/cover
- name: Tests
run: go test ./config ./auth ./db ./handlers ./routes ./utils -race -v -coverprofile=coverage.out && ./cover-check.sh coverage.out 2.9
14 changes: 14 additions & 0 deletions cover-check.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/sh

# Ref:
# - https://pretzelhands.com/posts/command-line-flags

# Usage:
# go test -race -v -coverprofile=coverage.out
# ./cover-check.sh coverage.out 70


PROFILE=$1
THRESHOLD=$2
COVERAGE=$(go tool cover -func=$PROFILE|grep total|awk '{print substr($3, 1, length($3) - 1)}')
echo "$COVERAGE $THRESHOLD" | awk '{if (!($1 >= $2)) { print "Coverage: " $1 "%" ", Expected threshold: " $2 "%"; exit 1 } }'
64 changes: 31 additions & 33 deletions db/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ func (db database) GetListedPosts(r *http.Request) ([]PeopleExtra, error) {
return ms, result.Error
}

func (db database) GetBountiesCount(personKey string, tabType string) int64 {
func (db database) GetUserBountiesCount(personKey string, tabType string) int64 {
var count int64

query := db.db.Model(&Bounty{})
Expand All @@ -437,28 +437,34 @@ func (db database) GetBountiesCount(personKey string, tabType string) int64 {
return count
}

func (db database) GetOrganizationBounties(r *http.Request, org_uuid string) []BountyData {
func (db database) GetBountiesCount() int64 {
var count int64
db.db.Model(&Bounty{}).Where("show != ?", false).Count(&count)
return count
}

func (db database) GetOrganizationBounties(r *http.Request, org_uuid string) []Bounty {
keys := r.URL.Query()
tags := keys.Get("tags") // this is a string of tags separated by commas
offset, limit, sortBy, direction, search := utils.GetPaginationParams(r)
ms := []BountyData{}
ms := []Bounty{}

orderQuery := ""
limitQuery := ""
searchQuery := ""
if sortBy != "" && direction != "" {
orderQuery = "ORDER BY " + "body." + sortBy + " " + direction
orderQuery = "ORDER BY " + sortBy + " " + direction
} else {
orderQuery = " ORDER BY " + "body." + sortBy + "" + "DESC"
orderQuery = " ORDER BY " + sortBy + "" + "DESC"
}
if offset != 0 && limit != 0 {
limitQuery = fmt.Sprintf("LIMIT %d OFFSET %d", limit, offset)
}
if search != "" {
searchQuery = fmt.Sprintf("WHERE LOWER(body.title) LIKE %s", "'%"+search+"%'")
searchQuery = fmt.Sprintf("WHERE LOWER(title) LIKE %s", "'%"+search+"%'")
}

rawQuery := "SELECT body.*, body.id as bounty_id, body.description as bounty_description, body.created as bounty_created, body.updated as bounty_updated, body.org_uuid, person.*, person.owner_alias as assignee_alias, person.id as assignee_id, person.description as assignee_description, person.created as assignee_created, person.updated as assignee_updated, person.owner_route_hint as assignee_route_hint, owner.id as bounty_owner_id, owner.uuid as owner_uuid, owner.owner_pub_key as owner_key, owner.owner_alias as owner_alias, owner.description as owner_description, owner.price_to_meet as owner_price_to_meet, owner.unique_name as owner_unique_name, owner.tags as owner_tags, owner.img as owner_img, owner.created as owner_created, owner.updated as owner_updated, owner.last_login as owner_last_login, owner.owner_route_hint as owner_route_hint, owner.owner_contact_key as owner_contact_key, org.name as organization_name, org.uuid as organization_uuid, org.img as organization_img FROM public.bounty AS body LEFT OUTER JOIN public.people AS person ON body.assignee = person.owner_pub_key LEFT OUTER JOIN public.people as owner ON body.owner_id = owner.owner_pub_key LEFT OUTER JOIN public.organizations as org ON body.org_uuid = org.uuid WHERE body.org_uuid =" + `'` + org_uuid + `'`
rawQuery := `SELECT * FROM bounty WHERE org_uuid = '` + org_uuid + `'`

theQuery := db.db.Raw(rawQuery + " " + searchQuery + " " + orderQuery + " " + limitQuery)

Expand All @@ -475,35 +481,27 @@ func (db database) GetOrganizationBounties(r *http.Request, org_uuid string) []B
return ms
}

func (db database) GetAssignedBounties(pubkey string) ([]BountyData, error) {
ms := []BountyData{}

err := db.db.Raw(`SELECT body.*, body.id as bounty_id, body.description as bounty_description, body.created as bounty_created, body.updated as bounty_updated, body.org_uuid, person.*, person.owner_alias as assignee_alias, person.id as assignee_id, person.description as assignee_description, person.created as assignee_created, person.updated as assignee_updated, person.owner_route_hint as assignee_route_hint, owner.id as bounty_owner_id, owner.uuid as owner_uuid, owner.owner_pub_key as owner_key, owner.owner_alias as owner_alias, owner.description as owner_description, owner.price_to_meet as owner_price_to_meet, owner.unique_name as owner_unique_name, owner.tags as owner_tags, owner.img as owner_img, owner.created as owner_created, owner.updated as owner_updated, owner.last_login as owner_last_login, owner.owner_route_hint as owner_route_hint, owner.owner_contact_key as owner_contact_key, org.name as organization_name, org.uuid as organization_uuid, org.img as organization_img FROM public.bounty AS body LEFT OUTER JOIN public.people AS person ON body.assignee = person.owner_pub_key LEFT OUTER JOIN public.people as owner ON body.owner_id = owner.owner_pub_key LEFT OUTER JOIN public.organizations as org ON body.org_uuid = org.uuid WHERE body.assignee = '` + pubkey + `' AND body.show != false ORDER BY body.id DESC`).Find(&ms).Error

func (db database) GetAssignedBounties(pubkey string) ([]Bounty, error) {
ms := []Bounty{}
err := db.db.Raw(`SELECT * FROM public.bounty WHERE assignee = '` + pubkey + `' AND show != false ORDER BY id DESC`).Find(&ms).Error
return ms, err
}

func (db database) GetCreatedBounties(pubkey string) ([]BountyData, error) {
ms := []BountyData{}

err := db.db.Raw(`SELECT body.*, body.id as bounty_id, body.description as bounty_description, body.created as bounty_created, body.updated as bounty_updated, body.org_uuid, person.*, person.owner_alias as assignee_alias, person.id as assignee_id, person.description as assignee_description, person.created as assignee_created, person.updated as assignee_updated, person.owner_route_hint as assignee_route_hint, owner.id as bounty_owner_id, owner.uuid as owner_uuid, owner.owner_pub_key as owner_key, owner.owner_alias as owner_alias, owner.description as owner_description, owner.price_to_meet as owner_price_to_meet, owner.unique_name as owner_unique_name, owner.tags as owner_tags, owner.img as owner_img, owner.created as owner_created, owner.updated as owner_updated, owner.last_login as owner_last_login, owner.owner_route_hint as owner_route_hint, owner.owner_contact_key as owner_contact_key, org.name as organization_name, org.uuid as organization_uuid, org.img as organization_img FROM public.bounty AS body LEFT OUTER JOIN public.people AS person ON body.assignee = person.owner_pub_key LEFT OUTER JOIN public.people as owner ON body.owner_id = owner.owner_pub_key LEFT OUTER JOIN public.organizations as org ON body.org_uuid = org.uuid WHERE body.owner_id = '` + pubkey + `' ORDER BY body.id DESC`).Find(&ms).Error

func (db database) GetCreatedBounties(pubkey string) ([]Bounty, error) {
ms := []Bounty{}
err := db.db.Raw(`SELECT * FROM public.bounty WHERE owner_id = '` + pubkey + `' ORDER BY id DESC`).Find(&ms).Error
return ms, err
}

func (db database) GetBountyById(id string) ([]BountyData, error) {
ms := []BountyData{}

err := db.db.Raw(`SELECT body.*, body.id as bounty_id, body.description as bounty_description, body.created as bounty_created, body.updated as bounty_updated, body.org_uuid, person.*, person.owner_alias as assignee_alias, person.id as assignee_id, person.description as assignee_description, person.created as assignee_created, person.updated as assignee_updated, person.owner_route_hint as assignee_route_hint, owner.id as bounty_owner_id, owner.uuid as owner_uuid, owner.owner_pub_key as owner_key, owner.owner_alias as owner_alias, owner.description as owner_description, owner.price_to_meet as owner_price_to_meet, owner.unique_name as owner_unique_name, owner.tags as owner_tags, owner.img as owner_img, owner.created as owner_created, owner.updated as owner_updated, owner.last_login as owner_last_login, owner.owner_route_hint as owner_route_hint, owner.owner_contact_key as owner_contact_key, org.name as organization_name, org.uuid as organization_uuid, org.img as organization_img FROM public.bounty AS body LEFT OUTER JOIN public.people AS person ON body.assignee = person.owner_pub_key LEFT OUTER JOIN public.people as owner ON body.owner_id = owner.owner_pub_key LEFT OUTER JOIN public.organizations as org ON body.org_uuid = org.uuid WHERE body.id = '` + id + `' ORDER BY body.id DESC`).Find(&ms).Error

func (db database) GetBountyById(id string) ([]Bounty, error) {
ms := []Bounty{}
err := db.db.Raw(`SELECT * FROM public.bounty WHERE id = '` + id + `'`).Find(&ms).Error
return ms, err
}

func (db database) GetBountyDataByCreated(created string) ([]BountyData, error) {
ms := []BountyData{}

err := db.db.Raw(`SELECT body.*, body.id as bounty_id, body.description as bounty_description, body.created as bounty_created, body.updated as bounty_updated, body.org_uuid, person.*, person.owner_alias as assignee_alias, person.id as assignee_id, person.description as assignee_description, person.created as assignee_created, person.updated as assignee_updated, person.owner_route_hint as assignee_route_hint, owner.id as bounty_owner_id, owner.uuid as owner_uuid, owner.owner_pub_key as owner_key, owner.owner_alias as owner_alias, owner.description as owner_description, owner.price_to_meet as owner_price_to_meet, owner.unique_name as owner_unique_name, owner.tags as owner_tags, owner.img as owner_img, owner.created as owner_created, owner.updated as owner_updated, owner.last_login as owner_last_login, owner.owner_route_hint as owner_route_hint, owner.owner_contact_key as owner_contact_key, org.name as organization_name, org.uuid as organization_uuid, org.img as organization_img FROM public.bounty AS body LEFT OUTER JOIN public.people AS person ON body.assignee = person.owner_pub_key LEFT OUTER JOIN public.people as owner ON body.owner_id = owner.owner_pub_key LEFT OUTER JOIN public.organizations as org ON body.org_uuid = org.uuid WHERE body.created = '` + created + `' ORDER BY body.id DESC`).Find(&ms).Error

func (db database) GetBountyDataByCreated(created string) ([]Bounty, error) {
ms := []Bounty{}
err := db.db.Raw(`SELECT * FROM public.bounty WHERE created = '` + created + `'`).Find(&ms).Error
return ms, err
}

Expand All @@ -512,30 +510,30 @@ func (db database) AddBounty(b Bounty) (Bounty, error) {
return b, nil
}

func (db database) GetAllBounties(r *http.Request) []BountyData {
func (db database) GetAllBounties(r *http.Request) []Bounty {
keys := r.URL.Query()
tags := keys.Get("tags") // this is a string of tags separated by commas
offset, limit, sortBy, direction, search := utils.GetPaginationParams(r)

ms := []BountyData{}
ms := []Bounty{}

orderQuery := ""
limitQuery := ""
searchQuery := ""

if sortBy != "" && direction != "" {
orderQuery = "ORDER BY " + "body." + sortBy + " " + direction
orderQuery = "ORDER BY " + sortBy + " " + direction
} else {
orderQuery = " ORDER BY " + "body." + sortBy + "" + "DESC"
orderQuery = " ORDER BY " + sortBy + "" + "DESC"
}
if limit != 0 {
limitQuery = fmt.Sprintf("LIMIT %d OFFSET %d", limit, offset)
}
if search != "" {
searchQuery = fmt.Sprintf("AND LOWER(body.title) LIKE %s", "'%"+search+"%'")
searchQuery = fmt.Sprintf("AND LOWER(title) LIKE %s", "'%"+search+"%'")
}

query := "SELECT body.*, body.id as bounty_id, body.description as bounty_description, body.created as bounty_created, body.updated as bounty_updated, body.org_uuid, person.*, person.owner_alias as assignee_alias, person.id as assignee_id, person.description as assignee_description, person.created as assignee_created, person.updated as assignee_updated, person.owner_route_hint as assignee_route_hint, owner.id as bounty_owner_id, owner.uuid as owner_uuid, owner.owner_pub_key as owner_key, owner.owner_alias as owner_alias, owner.description as owner_description, owner.price_to_meet as owner_price_to_meet, owner.unique_name as owner_unique_name, owner.tags as owner_tags, owner.img as owner_img, owner.created as owner_created, owner.updated as owner_updated, owner.last_login as owner_last_login, owner.owner_route_hint as owner_route_hint, owner.owner_contact_key as owner_contact_key, org.name as organization_name, org.uuid as organization_uuid, org.img as organization_img FROM public.bounty AS body LEFT OUTER JOIN public.people AS person ON body.assignee = person.owner_pub_key LEFT OUTER JOIN public.people as owner ON body.owner_id = owner.owner_pub_key LEFT OUTER JOIN public.organizations as org ON body.org_uuid = org.uuid WHERE body.show != false"
query := "SELECT * FROM public.bounty WHERE show != false"

allQuery := query + " " + searchQuery + " " + orderQuery + " " + limitQuery

Expand Down
55 changes: 50 additions & 5 deletions frontend/app/src/bounties/BountyDescription.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import { EuiText } from '@elastic/eui';
import React, { useEffect, useState } from 'react';
import styled from 'styled-components';
import { isString } from 'lodash';
import { OrganizationText, OrganizationWrap } from 'people/utils/style';
import { Link } from 'react-router-dom';
import { useStores } from 'store';
import { Organization } from 'store/main';
import { colors } from '../config/colors';
import { LanguageObject } from '../people/utils/languageLabelStyle';
import NameTag from '../people/utils/NameTag';
Expand All @@ -26,12 +30,13 @@ const BountyDescriptionContainer = styled.div<bounty_description_props>`
min-width: 519px;
max-width: 519px;
padding-left: 17px;
padding-right: 17px;
`;

const Header = styled.div`
display: flex;
flex-direction: row;
align-item: center;
justify-content: space-between;
height: 32px;
margin-top: 16px;
.NameContainer {
Expand All @@ -43,7 +48,7 @@ const Header = styled.div`
const Description = styled.div<bounty_description_props>`
display: flex;
flex-direction: row;
align-item: center;
align-items: center;
justify-content: space-between;
.DescriptionContainer {
display: flex;
Expand Down Expand Up @@ -93,24 +98,45 @@ const CodingLabels = styled.div<codingLangProps>`
margin-right: 4px;
.LanguageText {
font-size: 13px;
fontweight: 500;
font-weight: 500;
text-align: center;
font-family: 'Barlow';
line-height: 16px;
}
`;

const Img = styled.div<{
readonly src: string;
}>`
background-image: url('${(p: any) => p.src}');
background-position: center;
background-size: cover;
width: 20px;
height: 20px;
border-radius: 50%;
`;

const BountyDescription = (props: BountiesDescriptionProps) => {
const color = colors['light'];
const [dataValue, setDataValue] = useState([]);
const [replitLink, setReplitLink] = useState('');
const [descriptionImage, setDescriptionImage] = useState('');
const [org, setOrg] = useState<Organization | undefined>(undefined);
const { main } = useStores();

const fetchOrg = async () => {
if (!props.org_uuid) return;
const org = await main.getUserOrganizationByUuid(props.org_uuid);
setOrg(org);
};

useEffect(() => {
fetchOrg();
if (props.description) {
const found = props?.description.match(/(https?:\/\/.*\.(?:png|jpg|jpeg|gif))/);
const found = props?.description.match(/(https?:\/\/.*\.(?:png|jpg|jpeg|gif))(?![^`]*`)/);
setReplitLink(
props?.description.match(
/https?:\/\/(www\.)?[replit]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/
/https?:\/\/(?:www\.)?(?:replit\.[a-zA-Z0-9()]{1,256}|replit\.it)\b([-a-zA-Z0-9()@:%_+.~#?&//=]*)/
)
);
setDescriptionImage(found && found.length > 0 && found[0]);
Expand Down Expand Up @@ -148,6 +174,25 @@ const BountyDescription = (props: BountiesDescriptionProps) => {
org_uuid={props.uuid}
/>
</div>
{props.org_uuid && props.name && (
<Link to={`/org/bounties/${props.org_uuid}`} target="_blank">
<OrganizationWrap>
<Img
title={`${props.name} logo`}
src={org?.img || '/static/person_placeholder.png'}
/>
<OrganizationText>{props.name}</OrganizationText>
<img
className="buttonImage"
src={'/static/github_ticket.svg'}
alt={'github_ticket'}
height={'10px'}
width={'10px'}
style={{ transform: 'translateY(1px)' }}
/>
</OrganizationWrap>
</Link>
)}
</Header>
<Description isPaid={props?.isPaid} color={color}>
<div
Expand Down
1 change: 1 addition & 0 deletions frontend/app/src/bounties/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export interface BountiesDescriptionProps {
created?: number;
name?: string;
uuid?: string;
org_uuid?: string;
}

export interface BountiesPriceProps {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,7 @@ const InvitePeopleSearch = (props: InvitePeopleSearchProps) => {
</InvitedButton>
) : (
<ImageButton
buttonText={props.newDesign ? 'Assign' : 'Invite'}
buttonText={props.newDesign ? 'Assign' : 'Assign'}
ButtonContainerStyle={{
width: '86px',
height: '30px',
Expand Down
3 changes: 2 additions & 1 deletion frontend/app/src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { MainLayout } from './MainLayout';
import { Modals } from './Modals';
import { People } from './people';
import { TicketsPage } from './tickets';
import { OrgTicketsPage } from './tickets/org';
import { LeaderboardPage } from './leaderboard';

const modeDispatchPages: Record<AppMode, () => React.ReactElement> = {
Expand All @@ -40,7 +41,7 @@ const modeDispatchPages: Record<AppMode, () => React.ReactElement> = {
<TicketsPage />
</Route>
<Route path="/org/bounties/:uuid">
<TicketsPage />
<OrgTicketsPage />
</Route>
<Route path="/leaderboard">
<LeaderboardPage />
Expand Down
2 changes: 1 addition & 1 deletion frontend/app/src/pages/tickets/TicketModalPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export const TicketModalPage = observer(({ setConnectPerson }: Props) => {
const [isDeleted, setisDeleted] = useState(false);

const isMobile = useIsMobile();
const { uuid } = useParams<{ uuid: string }>();

const search = useMemo(() => {
const s = new URLSearchParams(location.search);
Expand Down Expand Up @@ -79,7 +80,6 @@ export const TicketModalPage = observer(({ setConnectPerson }: Props) => {
const goBack = async () => {
setVisible(false);
setisDeleted(false);
await main.getPeopleBounties({ page: 1, resetPage: true });
history.goBack();
};

Expand Down
Loading

0 comments on commit 7659c6b

Please sign in to comment.