more vocab work
This commit is contained in:
parent
00ec52a9a4
commit
84e5c926df
9 changed files with 111 additions and 20 deletions
|
@ -151,8 +151,7 @@ pub async fn get_kanji(
|
|||
if let Some(character) = &opts.character {
|
||||
query = query.bind(character.clone());
|
||||
}
|
||||
query = query.bind(opts.skip);
|
||||
query = query.bind(opts.how_many);
|
||||
query = query.bind(opts.skip).bind(opts.how_many);
|
||||
|
||||
let result = query
|
||||
.fetch_all(&kanji_db.0)
|
||||
|
|
|
@ -5,14 +5,15 @@ use crate::kanji::KanjiDb;
|
|||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Vocab {
|
||||
id: u32,
|
||||
kanji_writing: String,
|
||||
kana_writing: String,
|
||||
furigana: String,
|
||||
is_common: bool,
|
||||
|
||||
jlpt_level: u32,
|
||||
wanikani_level: u32,
|
||||
frequency_rank: u32,
|
||||
jlpt_level: Option<u32>,
|
||||
wanikani_level: Option<u32>,
|
||||
frequency_rank: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Derivative, Serialize, Deserialize)]
|
||||
|
@ -20,6 +21,21 @@ pub struct Vocab {
|
|||
pub struct GetVocabOptions {
|
||||
#[serde(default)]
|
||||
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)]
|
||||
|
@ -35,19 +51,55 @@ pub async fn get_vocab(
|
|||
) -> Result<GetVocabResult, String> {
|
||||
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!(
|
||||
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
|
||||
let count = {
|
||||
let kanji_filter_clause = match opts.kanji_id {
|
||||
Some(_) => format!(
|
||||
r#"
|
||||
JOIN KanjiEntityVocabEntity ON KanjiEntityVocabEntity.Vocabs_ID = VocabSet.ID
|
||||
WHERE KanjiEntityVocabEntity.Kanji_ID = ?
|
||||
"#
|
||||
JOIN KanjiEntityVocabEntity ON KanjiEntityVocabEntity.Vocabs_ID = VocabSet.ID
|
||||
WHERE KanjiEntityVocabEntity.Kanji_ID = ?
|
||||
"#
|
||||
),
|
||||
None => String::new(),
|
||||
};
|
||||
|
@ -73,8 +125,5 @@ pub async fn get_vocab(
|
|||
row.get("Count")
|
||||
};
|
||||
|
||||
Ok(GetVocabResult {
|
||||
count,
|
||||
vocab: vec![],
|
||||
})
|
||||
Ok(GetVocabResult { count, vocab })
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
.groupHeader {
|
||||
h1 {
|
||||
font-size: 2.5em;
|
||||
text-shadow: 1px 1px black;
|
||||
text-shadow: 1px 2px black;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,7 @@
|
|||
justify-content: space-evenly;
|
||||
|
||||
h3 {
|
||||
border-radius: 4px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.5);
|
||||
background-color: rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
|
|
|
@ -82,6 +82,8 @@ $kanjiDisplaySize: 120px;
|
|||
|
||||
.vocabSection {
|
||||
flex-grow: 1;
|
||||
min-height: 0;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
@ -92,5 +94,6 @@ $kanjiDisplaySize: 120px;
|
|||
|
||||
.vocabList {
|
||||
flex-grow: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
$vocabColor: rgb(228, 251, 215);
|
||||
|
||||
.main {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
@ -7,9 +9,25 @@
|
|||
gap: 8px;
|
||||
}
|
||||
|
||||
.searchBar {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.vocabList {
|
||||
flex-grow: 1;
|
||||
border: 1px solid lightgray;
|
||||
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%);
|
||||
}
|
||||
|
|
|
@ -24,13 +24,21 @@ export default function VocabList({ className, kanjiId }: VocabListProps) {
|
|||
|
||||
return (
|
||||
<main className={classNames(styles.main, className)}>
|
||||
<Input placeholder="Filter..." autoFocus />
|
||||
<Input placeholder="Filter..." autoFocus className={styles.searchBar} />
|
||||
|
||||
<div>
|
||||
Displaying {data.vocab.length} of {data.count} results. ({kanjiId})
|
||||
</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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,4 +3,12 @@ export interface GetVocabResult {
|
|||
vocab: Vocab[];
|
||||
}
|
||||
|
||||
export interface Vocab {}
|
||||
export interface Vocab {
|
||||
id: number;
|
||||
kanji_writing: string;
|
||||
kana_writing: string;
|
||||
furigana: string;
|
||||
|
||||
jlpt_level?: number;
|
||||
wanikani_level?: number;
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
padding: 16px;
|
||||
}
|
||||
|
||||
.test-word {
|
||||
.testWord {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 4em;
|
||||
|
@ -19,9 +19,11 @@
|
|||
|
||||
.needHelp {
|
||||
margin-top: 16px;
|
||||
display: flex;
|
||||
|
||||
summary {
|
||||
cursor: pointer;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,5 +38,8 @@
|
|||
.possibleAnswer {
|
||||
border: 1px solid rgba(0, 0, 0, 0.25);
|
||||
font-size: 1.1em;
|
||||
padding: 4px 8px;
|
||||
padding: 4px 12px;
|
||||
border-radius: 4px;
|
||||
background-color: rgba(255, 255, 255, 0.25);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
|
@ -147,7 +147,7 @@ export function Component() {
|
|||
/>
|
||||
)}
|
||||
|
||||
<h1 className={styles["test-word"]}>{nextItem.challenge}</h1>
|
||||
<h1 className={styles.testWord}>{nextItem.challenge}</h1>
|
||||
|
||||
<InputBox
|
||||
submit={formSubmit}
|
||||
|
|
Loading…
Reference in a new issue