mirror of
https://github.com/CompassConnections/Compass.git
synced 2026-03-24 17:41:27 -04:00
Make votes sortable
This commit is contained in:
@@ -33,9 +33,15 @@ CREATE INDEX vote_id_idx ON vote_results (vote_id);
|
||||
DROP INDEX IF EXISTS idx_vote_results_vote_choice;
|
||||
CREATE INDEX idx_vote_results_vote_choice ON vote_results (vote_id, choice);
|
||||
|
||||
DROP INDEX IF EXISTS idx_vote_results_vote_choice_priority;
|
||||
CREATE INDEX idx_vote_results_vote_choice_priority ON vote_results (vote_id, choice, priority);
|
||||
|
||||
DROP INDEX IF EXISTS idx_votes_created_time;
|
||||
CREATE INDEX idx_votes_created_time ON votes (created_time DESC);
|
||||
|
||||
|
||||
drop function if exists get_votes_with_results;
|
||||
create or replace function get_votes_with_results()
|
||||
create or replace function get_votes_with_results(order_by text default 'recent')
|
||||
returns table (
|
||||
id BIGINT,
|
||||
title text,
|
||||
@@ -49,21 +55,40 @@ create or replace function get_votes_with_results()
|
||||
priority int
|
||||
)
|
||||
as $$
|
||||
with results as (
|
||||
SELECT
|
||||
v.id,
|
||||
v.title,
|
||||
v.description,
|
||||
v.created_time,
|
||||
v.creator_id,
|
||||
v.is_anonymous,
|
||||
COALESCE(SUM(CASE WHEN r.choice = 1 THEN 1 ELSE 0 END), 0) AS votes_for,
|
||||
COALESCE(SUM(CASE WHEN r.choice = -1 THEN 1 ELSE 0 END), 0) AS votes_against,
|
||||
COALESCE(SUM(CASE WHEN r.choice = 0 THEN 1 ELSE 0 END), 0) AS votes_abstain,
|
||||
COALESCE(SUM(r.priority), 0)::float / GREATEST(COALESCE(SUM(CASE WHEN r.choice = 1 THEN 1 ELSE 0 END), 1), 1) * 100 / 3 AS priority
|
||||
FROM votes v
|
||||
LEFT JOIN vote_results r ON v.id = r.vote_id
|
||||
GROUP BY v.id
|
||||
)
|
||||
SELECT
|
||||
v.id,
|
||||
v.title,
|
||||
v.description,
|
||||
v.created_time,
|
||||
v.creator_id,
|
||||
v.is_anonymous,
|
||||
COALESCE(SUM(CASE WHEN r.choice = 1 THEN 1 ELSE 0 END), 0) AS votes_for,
|
||||
COALESCE(SUM(CASE WHEN r.choice = -1 THEN 1 ELSE 0 END), 0) AS votes_against,
|
||||
COALESCE(SUM(CASE WHEN r.choice = 0 THEN 1 ELSE 0 END), 0) AS votes_abstain,
|
||||
coalesce(SUM(r.priority), 0) AS priority
|
||||
FROM votes v
|
||||
LEFT JOIN vote_results r ON v.id = r.vote_id
|
||||
GROUP BY v.id
|
||||
ORDER BY v.created_time DESC;
|
||||
id,
|
||||
title,
|
||||
description,
|
||||
created_time,
|
||||
creator_id,
|
||||
is_anonymous,
|
||||
votes_for,
|
||||
votes_against,
|
||||
votes_abstain,
|
||||
priority
|
||||
FROM results
|
||||
ORDER BY
|
||||
CASE WHEN order_by = 'recent' THEN created_time END DESC,
|
||||
CASE WHEN order_by = 'mostVoted' THEN (votes_for + votes_against + votes_abstain) END DESC,
|
||||
CASE WHEN order_by = 'mostVoted' THEN created_time END DESC,
|
||||
CASE WHEN order_by = 'priority' THEN priority END DESC,
|
||||
CASE WHEN order_by = 'priority' THEN created_time END DESC;
|
||||
$$ language sql stable;
|
||||
|
||||
|
||||
|
||||
@@ -20,3 +20,4 @@ export const pStyle = "mt-1 text-gray-800 dark:text-white whitespace-pre-line";
|
||||
export const IS_MAINTENANCE = false; // set to true to enable maintenance mode banner
|
||||
|
||||
export const MIN_BIO_LENGTH = 250;
|
||||
|
||||
|
||||
7
common/src/votes/constants.ts
Normal file
7
common/src/votes/constants.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export const ORDER_BY = ['recent', 'mostVoted', 'priority'] as const
|
||||
export type OrderBy = typeof ORDER_BY[number]
|
||||
export const Constants: Record<OrderBy, string> = {
|
||||
recent: 'Most recent',
|
||||
mostVoted: 'Most voted',
|
||||
priority: 'Highest Priority',
|
||||
}
|
||||
@@ -20,11 +20,11 @@ function VoteButton(props: {
|
||||
<Button
|
||||
size="xs"
|
||||
disabled={disabled}
|
||||
className={clsx('px-4 py-2 rounded-lg', color)}
|
||||
className={clsx('px-2 xs:px-4 py-2 rounded-lg', color)}
|
||||
onClick={onClick}
|
||||
color={'gray-white'}
|
||||
>
|
||||
<div className="font-semibold mx-2">{count}</div>
|
||||
<div className="font-semibold mx-1 xs:mx-2">{count}</div>
|
||||
<div className="text-sm">{title}</div>
|
||||
</Button>
|
||||
)
|
||||
@@ -102,7 +102,7 @@ export function VoteButtons(props: {
|
||||
}
|
||||
|
||||
return (
|
||||
<Row className={clsx('gap-4 mt-2', className)}>
|
||||
<Row className={clsx('gap-2 xs:gap-4 mt-2 flex-wrap', className)}>
|
||||
<div className="relative" ref={containerRef}>
|
||||
<VoteButton
|
||||
color={clsx('bg-green-700 text-white hover:bg-green-500')}
|
||||
|
||||
@@ -16,13 +16,15 @@ import {Vote, VoteItem} from 'web/components/votes/vote-item'
|
||||
import Link from "next/link";
|
||||
import {formLink} from "common/constants";
|
||||
import { ShowMore } from "../widgets/show-more";
|
||||
import {ORDER_BY, Constants, OrderBy} from "common/votes/constants";
|
||||
|
||||
export function VoteComponent() {
|
||||
const user = useUser()
|
||||
const [orderBy, setOrderBy] = useState<OrderBy>('recent')
|
||||
|
||||
const {data: votes, refresh: refreshVotes} = useGetter(
|
||||
'votes',
|
||||
{},
|
||||
{orderBy},
|
||||
getVotes
|
||||
)
|
||||
|
||||
@@ -34,7 +36,24 @@ export function VoteComponent() {
|
||||
|
||||
return (
|
||||
<Col className="mx-2">
|
||||
<Title className="text-3xl">Proposals</Title>
|
||||
<Row className="items-center justify-between flex-col xxs:flex-row mb-4 xxs:mb-0 gap-2">
|
||||
<Title className="text-3xl">Proposals</Title>
|
||||
<div className="flex items-center gap-2 text-sm justify-end">
|
||||
<label htmlFor="orderBy" className="text-gray-600">Order by:</label>
|
||||
<select
|
||||
id="orderBy"
|
||||
value={orderBy}
|
||||
onChange={(e) => setOrderBy(e.target.value as OrderBy)}
|
||||
className="rounded-md border border-gray-300 px-2 py-1 text-sm bg-canvas-50"
|
||||
>
|
||||
{ORDER_BY.map((key) => (
|
||||
<option key={key} value={key}>
|
||||
{Constants[key]}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</Row>
|
||||
<p className={'customlink'}>
|
||||
You can discuss any of those proposals through the <Link href={'/contact'}>contact form</Link>, the <Link href={formLink}>feedback form</Link>, or any of our <Link href={'/social'}>socials</Link>.
|
||||
</p>
|
||||
|
||||
@@ -33,8 +33,8 @@ export function VoteItem(props: {
|
||||
<Col className='text-sm text-gray-500 italic'>
|
||||
<Content className="w-full" content={vote.description as JSONContent}/>
|
||||
</Col>
|
||||
<Row className={'gap-2 mt-2 items-center justify-between w-full customlink'}>
|
||||
{!!vote.priority ? <div>Priority: {((vote.priority / vote.votes_for / 3) * 100).toFixed(0)}%</div> : <p></p>}
|
||||
<Row className={'gap-2 mt-2 items-center justify-between w-full customlink flex-wrap'}>
|
||||
{!!vote.priority ? <div>Priority: {vote.priority.toFixed(0)}%</div> : <p></p>}
|
||||
{!vote.is_anonymous && creator?.username && <Link href={`/${creator.username}`} className="customlink">{creator.username}</Link>}
|
||||
</Row>
|
||||
<VoteButtons
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import {run} from 'common/supabase/utils'
|
||||
import {db} from 'web/lib/supabase/db'
|
||||
|
||||
export const getVotes = async () => {
|
||||
const {data, error} = await db.rpc('get_votes_with_results' as any);
|
||||
if (error) throw error;
|
||||
import {OrderBy} from "common/votes/constants";
|
||||
|
||||
// data.forEach((vote: any) => {
|
||||
// console.log(vote.title, vote.votes_for, vote.votes_against, vote.votes_abstain);
|
||||
// });
|
||||
export const getVotes = async (params: { orderBy: OrderBy }) => {
|
||||
const { orderBy } = params
|
||||
const {data, error} = await db.rpc('get_votes_with_results' as any, {
|
||||
order_by: orderBy,
|
||||
});
|
||||
if (error) throw error;
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
@@ -11,7 +11,18 @@ module.exports = {
|
||||
'./components/**/*.{js,ts,jsx,tsx}',
|
||||
],
|
||||
theme: {
|
||||
fontFamily: Object.assign(
|
||||
|
||||
screens: {
|
||||
xxs: '320px',
|
||||
xs: '480px',
|
||||
sm: '640px',
|
||||
md: '768px',
|
||||
lg: '1024px',
|
||||
xl: '1280px',
|
||||
'2xl': '1536px',
|
||||
},
|
||||
|
||||
fontFamily: Object.assign(
|
||||
{ ...defaultTheme.fontFamily },
|
||||
{
|
||||
'major-mono': ['var(--font-logo)', 'monospace'],
|
||||
|
||||
Reference in New Issue
Block a user