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 {
|
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)
|
||||||
|
|
|
@ -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![],
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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%);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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}
|
||||||
|
|
Loading…
Reference in a new issue