more vocab work

This commit is contained in:
Michael Zhang 2023-06-15 00:32:12 -05:00
parent 00ec52a9a4
commit 84e5c926df
9 changed files with 111 additions and 20 deletions

View file

@ -151,8 +151,7 @@ pub async fn get_kanji(
if let Some(character) = &opts.character { if let Some(character) = &opts.character {
query = query.bind(character.clone()); query = query.bind(character.clone());
} }
query = query.bind(opts.skip); query = query.bind(opts.skip).bind(opts.how_many);
query = query.bind(opts.how_many);
let result = query let result = query
.fetch_all(&kanji_db.0) .fetch_all(&kanji_db.0)

View file

@ -5,14 +5,15 @@ use crate::kanji::KanjiDb;
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct Vocab { pub struct Vocab {
id: u32,
kanji_writing: String, kanji_writing: String,
kana_writing: String, kana_writing: String,
furigana: String, furigana: String,
is_common: bool, is_common: bool,
jlpt_level: u32, jlpt_level: Option<u32>,
wanikani_level: u32, wanikani_level: Option<u32>,
frequency_rank: u32, frequency_rank: Option<u32>,
} }
#[derive(Debug, Derivative, Serialize, Deserialize)] #[derive(Debug, Derivative, Serialize, Deserialize)]
@ -20,6 +21,21 @@ pub struct Vocab {
pub struct GetVocabOptions { pub struct GetVocabOptions {
#[serde(default)] #[serde(default)]
kanji_id: Option<u32>, kanji_id: Option<u32>,
#[serde(default = "default_skip")]
#[derivative(Default(value = "0"))]
skip: u32,
#[serde(default = "default_how_many")]
#[derivative(Default(value = "40"))]
how_many: u32,
}
fn default_skip() -> u32 {
0
}
fn default_how_many() -> u32 {
40
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
@ -35,19 +51,55 @@ pub async fn get_vocab(
) -> Result<GetVocabResult, String> { ) -> Result<GetVocabResult, String> {
let opts = options.unwrap_or_default(); let opts = options.unwrap_or_default();
let kanji_filter_clause = match opts.kanji_id {
Some(_) => format!(
r#"
JOIN KanjiEntityVocabEntity ON KanjiEntityVocabEntity.Vocabs_ID = VocabSet.ID
WHERE KanjiEntityVocabEntity.Kanji_ID = ?
"#
),
None => String::new(),
};
let query_string = format!( let query_string = format!(
r#" r#"
SELECT * FROM VocabSet
{kanji_filter_clause}
LIMIT ?, ?
"# "#
); );
let mut query = sqlx::query(&query_string);
if let Some(kanji_id) = opts.kanji_id {
query = query.bind(kanji_id);
}
query = query.bind(opts.skip).bind(opts.how_many);
let rows = query
.fetch_all(&kanji_db.0)
.await
.map_err(|err| err.to_string())?;
let vocab = rows
.into_iter()
.map(|row| Vocab {
id: row.get("ID"),
kanji_writing: row.get("KanjiWriting"),
kana_writing: row.get("KanaWriting"),
furigana: row.get("Furigana"),
is_common: row.get("IsCommon"),
jlpt_level: row.get("JlptLevel"),
wanikani_level: row.get("WkLevel"),
frequency_rank: row.get("FrequencyRank"),
})
.collect();
// Count query // Count query
let count = { let count = {
let kanji_filter_clause = match opts.kanji_id { let kanji_filter_clause = match opts.kanji_id {
Some(_) => format!( Some(_) => format!(
r#" r#"
JOIN KanjiEntityVocabEntity ON KanjiEntityVocabEntity.Vocabs_ID = VocabSet.ID JOIN KanjiEntityVocabEntity ON KanjiEntityVocabEntity.Vocabs_ID = VocabSet.ID
WHERE KanjiEntityVocabEntity.Kanji_ID = ? WHERE KanjiEntityVocabEntity.Kanji_ID = ?
"# "#
), ),
None => String::new(), None => String::new(),
}; };
@ -73,8 +125,5 @@ pub async fn get_vocab(
row.get("Count") row.get("Count")
}; };
Ok(GetVocabResult { Ok(GetVocabResult { count, vocab })
count,
vocab: vec![],
})
} }

View file

@ -20,7 +20,7 @@
.groupHeader { .groupHeader {
h1 { h1 {
font-size: 2.5em; font-size: 2.5em;
text-shadow: 1px 1px black; text-shadow: 1px 2px black;
} }
} }
@ -30,6 +30,7 @@
justify-content: space-evenly; justify-content: space-evenly;
h3 { h3 {
border-radius: 4px;
border: 1px solid rgba(255, 255, 255, 0.5); border: 1px solid rgba(255, 255, 255, 0.5);
background-color: rgba(255, 255, 255, 0.25); background-color: rgba(255, 255, 255, 0.25);
} }

View file

@ -82,6 +82,8 @@ $kanjiDisplaySize: 120px;
.vocabSection { .vocabSection {
flex-grow: 1; flex-grow: 1;
min-height: 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 8px; gap: 8px;
@ -92,5 +94,6 @@ $kanjiDisplaySize: 120px;
.vocabList { .vocabList {
flex-grow: 1; flex-grow: 1;
min-height: 0;
} }
} }

View file

@ -1,3 +1,5 @@
$vocabColor: rgb(228, 251, 215);
.main { .main {
width: 100%; width: 100%;
height: 100%; height: 100%;
@ -7,9 +9,25 @@
gap: 8px; gap: 8px;
} }
.searchBar {
flex-shrink: 0;
}
.vocabList { .vocabList {
flex-grow: 1; flex-grow: 1;
border: 1px solid lightgray; border: 1px solid lightgray;
padding: 12px; padding: 12px;
overflow-y: scroll; overflow-y: auto;
display: flex;
flex-direction: column;
gap: 12px;
}
.vocabEntry {
padding: 8px;
background-color: $vocabColor;
border: 1px solid darken($vocabColor, 10%);
border-radius: 4px;
// box-shadow: 0 1px 2px darken($vocabColor, 50%);
} }

View file

@ -24,13 +24,21 @@ export default function VocabList({ className, kanjiId }: VocabListProps) {
return ( return (
<main className={classNames(styles.main, className)}> <main className={classNames(styles.main, className)}>
<Input placeholder="Filter..." autoFocus /> <Input placeholder="Filter..." autoFocus className={styles.searchBar} />
<div> <div>
Displaying {data.vocab.length} of {data.count} results. ({kanjiId}) Displaying {data.vocab.length} of {data.count} results. ({kanjiId})
</div> </div>
<div className={styles.vocabList}></div> <div className={styles.vocabList}>
{data.vocab.map((vocab) => (
<div key={vocab.id} className={styles.vocabEntry}>
{vocab.kanji_writing}
<br />
{vocab.kana_writing}
</div>
))}
</div>
</main> </main>
); );
} }

View file

@ -3,4 +3,12 @@ export interface GetVocabResult {
vocab: Vocab[]; vocab: Vocab[];
} }
export interface Vocab {} export interface Vocab {
id: number;
kanji_writing: string;
kana_writing: string;
furigana: string;
jlpt_level?: number;
wanikani_level?: number;
}

View file

@ -10,7 +10,7 @@
padding: 16px; padding: 16px;
} }
.test-word { .testWord {
width: 100%; width: 100%;
text-align: center; text-align: center;
font-size: 4em; font-size: 4em;
@ -19,9 +19,11 @@
.needHelp { .needHelp {
margin-top: 16px; margin-top: 16px;
display: flex;
summary { summary {
cursor: pointer; cursor: pointer;
margin-bottom: 8px;
} }
} }
@ -36,5 +38,8 @@
.possibleAnswer { .possibleAnswer {
border: 1px solid rgba(0, 0, 0, 0.25); border: 1px solid rgba(0, 0, 0, 0.25);
font-size: 1.1em; font-size: 1.1em;
padding: 4px 8px; padding: 4px 12px;
border-radius: 4px;
background-color: rgba(255, 255, 255, 0.25);
cursor: pointer;
} }

View file

@ -147,7 +147,7 @@ export function Component() {
/> />
)} )}
<h1 className={styles["test-word"]}>{nextItem.challenge}</h1> <h1 className={styles.testWord}>{nextItem.challenge}</h1>
<InputBox <InputBox
submit={formSubmit} submit={formSubmit}