refactor: restructure api module and implement articles pagination with dynamic routing
This commit is contained in:
@@ -0,0 +1,38 @@
|
|||||||
|
import requests
|
||||||
|
|
||||||
|
API_BASE_URL = 'http://localhost:8000/api'
|
||||||
|
|
||||||
|
def list_articles(page=1, search=None, token=None):
|
||||||
|
params = {'page' : page}
|
||||||
|
|
||||||
|
if search:
|
||||||
|
params['search'] = search
|
||||||
|
|
||||||
|
headers = {}
|
||||||
|
if token:
|
||||||
|
headers['Authorization'] = f"Bearer {token}"
|
||||||
|
|
||||||
|
r = requests.get(
|
||||||
|
f"{API_BASE_URL}/articles",
|
||||||
|
params=params,
|
||||||
|
headers=headers,
|
||||||
|
timeout=10
|
||||||
|
)
|
||||||
|
|
||||||
|
r.raise_for_status()
|
||||||
|
return r.json()
|
||||||
|
|
||||||
|
def get_article(article_id, token=None):
|
||||||
|
headers={}
|
||||||
|
|
||||||
|
if token:
|
||||||
|
headers['Authorization'] = f"Bearer {token}"
|
||||||
|
|
||||||
|
r = requests.get(
|
||||||
|
f"{API_BASE_URL}/articles/{article_id}",
|
||||||
|
headers = headers,
|
||||||
|
timeout = 10
|
||||||
|
)
|
||||||
|
|
||||||
|
r.raise_for_status()
|
||||||
|
return r.json()
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import asyncio
|
||||||
|
|
||||||
|
import flet as ft
|
||||||
|
|
||||||
|
from app.api.articles import get_article
|
||||||
|
from app.state import state
|
||||||
|
|
||||||
|
|
||||||
|
def article_detail_page(page: ft.Page, article_id: int):
|
||||||
|
content = ft.Column(expand=True)
|
||||||
|
|
||||||
|
async def load_article():
|
||||||
|
content.controls.clear()
|
||||||
|
content.controls.append(ft.ProgressRing())
|
||||||
|
page.update()
|
||||||
|
|
||||||
|
article = await asyncio.to_thread(
|
||||||
|
get_article,
|
||||||
|
article_id,
|
||||||
|
state.access_token
|
||||||
|
)
|
||||||
|
|
||||||
|
content.controls.clear()
|
||||||
|
content.controls.extend([
|
||||||
|
ft.Text(article['title'], size=16, weight=ft.FontWeight.BOLD),
|
||||||
|
ft.Text(article['body']),
|
||||||
|
ft.FilledButton(
|
||||||
|
'ย้อนกลับ',
|
||||||
|
on_click=lambda e: asyncio.create_task(page.push_route('/articles'))
|
||||||
|
)
|
||||||
|
])
|
||||||
|
page.update()
|
||||||
|
|
||||||
|
asyncio.create_task(load_article())
|
||||||
|
return content
|
||||||
+105
-5
@@ -1,7 +1,107 @@
|
|||||||
|
import asyncio
|
||||||
|
|
||||||
import flet as ft
|
import flet as ft
|
||||||
|
|
||||||
def articles_page():
|
from app.api.articles import list_articles
|
||||||
return ft.Column([
|
|
||||||
ft.Text("บทความ", size=22),
|
from app.state import state
|
||||||
ft.Text("รายการบทความจะมาอยู่ตรงนี้"),
|
|
||||||
])
|
def article_card(page, article):
|
||||||
|
async def go_detail(e):
|
||||||
|
await page.push_route(f"/articles/{article['id']}")
|
||||||
|
|
||||||
|
return ft.Card(
|
||||||
|
content=ft.Container(
|
||||||
|
padding=15,
|
||||||
|
content=ft.Column(
|
||||||
|
controls=[
|
||||||
|
ft.Text(article['title'], size=18, weight=ft.FontWeight.BOLD),
|
||||||
|
ft.Text(article.get('body', ''), max_lines=2),
|
||||||
|
ft.TextButton(
|
||||||
|
'อ่านต่อ',
|
||||||
|
on_click=go_detail,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def articles_page(page: ft.Page):
|
||||||
|
articles_column = ft.Column(expand=True, spacing=10)
|
||||||
|
search_field = ft.TextField(
|
||||||
|
hint_text="ค้นหาบทความ",
|
||||||
|
prefix_icon=ft.Icons.SEARCH,
|
||||||
|
)
|
||||||
|
|
||||||
|
current_page = {'value':1}
|
||||||
|
|
||||||
|
page_text = ft.Text(value=f"หน้า {current_page['value']}")
|
||||||
|
# สร้างปุ่ม
|
||||||
|
btn_prev = ft.OutlinedButton('ก่อนหน้า', disabled=True)
|
||||||
|
btn_next = ft.OutlinedButton('ถัดไป', disabled=True)
|
||||||
|
|
||||||
|
async def load_articles():
|
||||||
|
articles_column.controls.clear()
|
||||||
|
articles_column.controls.append(ft.ProgressRing())
|
||||||
|
|
||||||
|
btn_prev.disabled = True
|
||||||
|
btn_next.disabled = True
|
||||||
|
|
||||||
|
page.update()
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = await asyncio.to_thread(
|
||||||
|
list_articles,
|
||||||
|
page=current_page['value'],
|
||||||
|
search=search_field.value,
|
||||||
|
token=state.access_token,
|
||||||
|
)
|
||||||
|
|
||||||
|
# อัปเดตสถานะปุ่มจากข้อมูล Backend
|
||||||
|
btn_prev.disabled = data.get('previous') is None
|
||||||
|
btn_next.disabled = data.get('next') is None
|
||||||
|
page_text.value = f"หน้า {current_page['value']}"
|
||||||
|
|
||||||
|
articles_column.controls.clear()
|
||||||
|
for item in data.get('results', []):
|
||||||
|
articles_column.controls.append(article_card(page, item))
|
||||||
|
|
||||||
|
if not data.get('results'):
|
||||||
|
articles_column.controls.append(ft.Text("ไม่พบบทความ"))
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
articles_column.controls.clear()
|
||||||
|
articles_column.controls.append(ft.Text(f"เกิดข้อผิดพลาด: {e}", color=ft.Colors.RED))
|
||||||
|
btn_prev.disabled = current_page['value'] <= 1
|
||||||
|
btn_next.disabled = True
|
||||||
|
|
||||||
|
page.update()
|
||||||
|
|
||||||
|
# Logic การเปลี่ยนหน้า
|
||||||
|
async def change_page(delta):
|
||||||
|
current_page['value'] += delta
|
||||||
|
await load_articles()
|
||||||
|
btn_prev.on_click = lambda e: asyncio.create_task(change_page(-1))
|
||||||
|
btn_next.on_click = lambda e: asyncio.create_task(change_page(1))
|
||||||
|
|
||||||
|
def on_search(e):
|
||||||
|
current_page['value'] = 1
|
||||||
|
asyncio.create_task(load_articles())
|
||||||
|
|
||||||
|
search_field.on_submit = on_search
|
||||||
|
|
||||||
|
controls = ft.Column(
|
||||||
|
controls=[
|
||||||
|
ft.Text('บทความ', size=24),
|
||||||
|
ft.Row([search_field]),
|
||||||
|
articles_column,
|
||||||
|
ft.Row(
|
||||||
|
controls=[btn_prev, page_text, btn_next],
|
||||||
|
alignment=ft.MainAxisAlignment.CENTER
|
||||||
|
)
|
||||||
|
],
|
||||||
|
expand=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
asyncio.create_task(load_articles())
|
||||||
|
return controls
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import flet as ft
|
import flet as ft
|
||||||
|
|
||||||
def courses_page():
|
def courses_page(page: ft.Page):
|
||||||
return ft.Column([
|
return ft.Column([
|
||||||
ft.Text("หลักสูตร", size=22),
|
ft.Text("หลักสูตร", size=22),
|
||||||
ft.Text("รายการหลักสูตร")
|
ft.Text("รายการหลักสูตร")
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
import flet as ft
|
import flet as ft
|
||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
from app.api import login, get_me
|
from app.api.auth import login, get_me
|
||||||
from app.state import state
|
from app.state import state
|
||||||
|
|
||||||
def login_page(page: ft.Page, on_login_success):
|
def login_page(page: ft.Page, on_login_success):
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import flet as ft
|
import flet as ft
|
||||||
|
|
||||||
def my_courses_page():
|
def my_courses_page(page: ft.Page):
|
||||||
return ft.Column([
|
return ft.Column([
|
||||||
ft.Text('หลักสูตรของฉัน', size=22),
|
ft.Text('หลักสูตรของฉัน', size=22),
|
||||||
ft.Text('ข้อมูลหลักสูตรที่ลงทะเบียน')
|
ft.Text('ข้อมูลหลักสูตรที่ลงทะเบียน')
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import flet as ft
|
import flet as ft
|
||||||
|
|
||||||
def profile_page():
|
def profile_page(page: ft.Page):
|
||||||
return ft.Column([
|
return ft.Column([
|
||||||
ft.Text("ข้อมูลผู้ใช้งาน", size=22),
|
ft.Text("ข้อมูลผู้ใช้งาน", size=22),
|
||||||
ft.Text("รายละเอียดข้อมูลผู้ใช้งานจะมาอยู่ตรงนี้"),
|
ft.Text("รายละเอียดข้อมูลผู้ใช้งานจะมาอยู่ตรงนี้"),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import flet as ft
|
import flet as ft
|
||||||
|
|
||||||
def progress_page():
|
def progress_page(page: ft.Page):
|
||||||
return ft.Column([
|
return ft.Column([
|
||||||
ft.Text('ความก้าวหน้าของฉัน', size=22),
|
ft.Text('ความก้าวหน้าของฉัน', size=22),
|
||||||
ft.Text('รายละเอียดความก้าวหน้าของฉัน')
|
ft.Text('รายละเอียดความก้าวหน้าของฉัน')
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import flet as ft
|
import flet as ft
|
||||||
|
|
||||||
from app.layout import main_layout
|
from app.layout import main_layout
|
||||||
|
from app.pages.article_detail import article_detail_page
|
||||||
from app.pages.login import login_page
|
from app.pages.login import login_page
|
||||||
from app.pages.articles import articles_page
|
from app.pages.articles import articles_page
|
||||||
from app.pages.courses import courses_page
|
from app.pages.courses import courses_page
|
||||||
@@ -27,6 +28,7 @@ async def main(page: ft.Page):
|
|||||||
# ROUTES MAP
|
# ROUTES MAP
|
||||||
routes = {
|
routes = {
|
||||||
"/articles": articles_page,
|
"/articles": articles_page,
|
||||||
|
"/articles/{id}": article_detail_page,
|
||||||
"/courses": courses_page,
|
"/courses": courses_page,
|
||||||
"/my_courses": my_courses_page,
|
"/my_courses": my_courses_page,
|
||||||
"/progress": progress_page,
|
"/progress": progress_page,
|
||||||
@@ -73,8 +75,16 @@ async def main(page: ft.Page):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# เปลี่ยน content อย่างเดียว
|
# เปลี่ยน content อย่างเดียว
|
||||||
if page.route in routes:
|
# จัดการ Dynamic Route ก่อน เช่น ARTICLE DETAIL
|
||||||
main_container.content = routes[page.route]()
|
if page.route.startswith("/articles/"):
|
||||||
|
try:
|
||||||
|
article_id = int(page.route.split('/')[-1])
|
||||||
|
main_container.content = article_detail_page(page, article_id)
|
||||||
|
except ValueError:
|
||||||
|
main_container.content = ft.Text('Invalid article ID')
|
||||||
|
# จัดการ Static Routes ที่เหลือ
|
||||||
|
elif page.route in routes:
|
||||||
|
main_container.content = routes[page.route](page)
|
||||||
else:
|
else:
|
||||||
main_container.content = ft.Text("404 NOT FOUND")
|
main_container.content = ft.Text("404 NOT FOUND")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user