feat(frontend): make field entries sortable

This commit is contained in:
Sam 2023-03-05 04:18:35 +01:00
parent 66a0830ef2
commit 11363d6769
No known key found for this signature in database
GPG key ID: B4EF20DDE721CAA1
3 changed files with 39 additions and 16 deletions

View file

@ -7,7 +7,6 @@ import (
"codeberg.org/u1f320/pronouns.cc/backend/db/queries" "codeberg.org/u1f320/pronouns.cc/backend/db/queries"
"emperror.dev/errors" "emperror.dev/errors"
"github.com/bwmarrin/discordgo" "github.com/bwmarrin/discordgo"
"github.com/georgysavva/scany/pgxscan"
"github.com/jackc/pgconn" "github.com/jackc/pgconn"
"github.com/jackc/pgx/v4" "github.com/jackc/pgx/v4"
"github.com/rs/xid" "github.com/rs/xid"
@ -89,20 +88,18 @@ func (db *DB) CreateUser(ctx context.Context, tx pgx.Tx, username string) (u Use
// DiscordUser fetches a user by Discord user ID. // DiscordUser fetches a user by Discord user ID.
func (db *DB) DiscordUser(ctx context.Context, discordID string) (u User, err error) { func (db *DB) DiscordUser(ctx context.Context, discordID string) (u User, err error) {
sql, args, err := sq.Select("*").From("users").Where("discord = ?", discordID).ToSql() sql, args, err := sq.Select("id").From("users").Where("discord = ?", discordID).ToSql()
if err != nil { if err != nil {
return u, errors.Wrap(err, "building sql") return u, errors.Wrap(err, "building sql")
} }
err = pgxscan.Get(ctx, db, &u, sql, args...) var id xid.ID
err = db.QueryRow(ctx, sql, args...).Scan(&id)
if err != nil { if err != nil {
if errors.Cause(err) == pgx.ErrNoRows { return u, errors.Wrap(err, "executing id query")
return u, ErrUserNotFound
}
return u, errors.Cause(err)
} }
return u, nil return db.getUser(ctx, db, id)
} }
func (u *User) UpdateFromDiscord(ctx context.Context, db querier, du *discordgo.User) error { func (u *User) UpdateFromDiscord(ctx context.Context, db querier, du *discordgo.User) error {

View file

@ -5,6 +5,7 @@ import {
Heart, Heart,
People, People,
Plus, Plus,
ThreeDotsVertical,
Trash3, Trash3,
} from "react-bootstrap-icons"; } from "react-bootstrap-icons";
@ -13,11 +14,18 @@ import TextInput from "./TextInput";
import Button, { ButtonStyle } from "./Button"; import Button, { ButtonStyle } from "./Button";
import { useState } from "react"; import { useState } from "react";
import { WordStatus } from "../lib/api-fetch"; import { WordStatus } from "../lib/api-fetch";
import { ReactSortable } from "react-sortablejs";
export interface EditField { export interface EditField {
id: number; id: number;
name: string; name: string;
values: Array<{ value: string; status: WordStatus }>; values: EditFieldValue[];
}
export interface EditFieldValue {
id: number;
value: string;
status: WordStatus;
} }
type EditableCardProps = { type EditableCardProps = {
@ -35,6 +43,7 @@ type EditableCardProps = {
onChangeFriends(e: React.MouseEvent<HTMLButtonElement>, index: number): void; onChangeFriends(e: React.MouseEvent<HTMLButtonElement>, index: number): void;
onChangeAvoid(e: React.MouseEvent<HTMLButtonElement>, index: number): void; onChangeAvoid(e: React.MouseEvent<HTMLButtonElement>, index: number): void;
onClickDelete: React.MouseEventHandler<HTMLButtonElement>; onClickDelete: React.MouseEventHandler<HTMLButtonElement>;
onChangeOrder(newState: EditFieldValue[]): void;
}; };
export function EditableCard(props: EditableCardProps) { export function EditableCard(props: EditableCardProps) {
@ -68,16 +77,21 @@ export function EditableCard(props: EditableCardProps) {
return ( return (
<Card title={props.field.name} draggable footer={footer}> <Card title={props.field.name} draggable footer={footer}>
<ul> <ReactSortable
handle=".entry-handle"
list={props.field.values}
setList={props.onChangeOrder}
>
{props.field.values.map((value, index) => { {props.field.values.map((value, index) => {
return ( return (
<li className="flex justify-between my-1 items-center" key={index}> <li className="flex justify-between my-1 items-center" key={index}>
<ThreeDotsVertical className="entry-handle hover:cursor-grab" />
<TextInput <TextInput
value={value.value} value={value.value}
prevValue={value.value} prevValue={value.value}
onChange={props.onChangePronoun} onChange={props.onChangePronoun}
/> />
<div className="rounded-md"> <div>
<button <button
type="button" type="button"
onClick={(e) => props.onChangeFavourite(e, index)} onClick={(e) => props.onChangeFavourite(e, index)}
@ -144,7 +158,7 @@ export function EditableCard(props: EditableCardProps) {
</li> </li>
); );
})} })}
</ul> </ReactSortable>
</Card> </Card>
); );
} }

View file

@ -10,7 +10,11 @@ import { ReactSortable } from "react-sortablejs";
import { useRecoilState, useRecoilValue } from "recoil"; import { useRecoilState, useRecoilValue } from "recoil";
import Button, { ButtonStyle } from "../../components/Button"; import Button, { ButtonStyle } from "../../components/Button";
import { EditableCard, EditField } from "../../components/Editable"; import {
EditableCard,
EditField,
EditFieldValue,
} from "../../components/Editable";
import Loading from "../../components/Loading"; import Loading from "../../components/Loading";
import { fetchAPI, Field, MeUser, WordStatus } from "../../lib/api-fetch"; import { fetchAPI, Field, MeUser, WordStatus } from "../../lib/api-fetch";
import { themeState, userState } from "../../lib/state"; import { themeState, userState } from "../../lib/state";
@ -30,8 +34,8 @@ export default function Index() {
values: [], values: [],
}; };
f.entries?.forEach((entry) => { f.entries?.forEach((entry, idx) => {
field.values.push(entry); field.values.push({ ...entry, id: idx });
}); });
return field; return field;
@ -168,7 +172,15 @@ export default function Index() {
setFields([...fields]); setFields([...fields]);
}} }}
onAddPronoun={(pronoun) => { onAddPronoun={(pronoun) => {
field.values.push({ value: pronoun, status: WordStatus.Okay }); field.values.push({
id: field.values.length + 1,
value: pronoun,
status: WordStatus.Okay,
});
setFields([...fields]);
}}
onChangeOrder={(newState: EditFieldValue[]) => {
field.values = newState;
setFields([...fields]); setFields([...fields]);
}} }}
onDeletePronoun={(e, index) => { onDeletePronoun={(e, index) => {