-
Couldn't load subscription status.
- Fork 0
Feat/manage user #42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Feat/manage user #42
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
942be57
feat:change url
Chulinuwu a0997e0
feat/mockManageUserPage
Chulinuwu 2c6a64f
fix:ui
Chulinuwu a843c07
feat:popup
Chulinuwu 37bcd91
feat:add pagination
Chulinuwu d958d10
fix:fixed layout
Chulinuwu b88a00b
feat:pagination not done yet , idk what to do
Chulinuwu 49f23b6
fix:ui
Chulinuwu 75eddb8
fix:lint
Chulinuwu f10b32c
fix:pagination1
Chulinuwu a2837fe
fix:pagination + filter user
Chulinuwu c831692
feat:user can filter maybe
Chulinuwu 9a45169
fix:lint
Chulinuwu 4f3c289
fix:lint
Chulinuwu File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| export async function getAllUsers(token: string) { | ||
| try { | ||
| const response = await fetch( | ||
| 'https://sucu-backend-2024-689509857491.asia-southeast1.run.app/api/v1/users', | ||
| { | ||
| method: 'GET', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| Authorization: `Bearer ${token}` | ||
| } | ||
| } | ||
| ); | ||
|
|
||
| if (!response.ok) { | ||
| const errorData = await response.json(); | ||
| throw new Error(`Error: ${errorData.message}`); | ||
| } | ||
|
|
||
| const data = await response.json(); | ||
| return data; | ||
| } catch (error) { | ||
| console.error('Failed to fetch users:', error); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,68 @@ | ||
| <script> | ||
| <script lang="ts"> | ||
| import Footer from '$lib/components/Footer/Footer.svelte'; | ||
| import Navbar from '$lib/components/Navbar.svelte'; | ||
| import Sidebar from '$lib/components/Sidebar/Sidebar.svelte'; | ||
| import { getMe } from '$lib/api/getme'; | ||
| import { getDecryptedToken } from '$lib/api/getToken'; | ||
| import '../styles/app.css'; | ||
| import { onMount } from 'svelte'; | ||
|
|
||
| let isAdminPage = false; | ||
| let isAuthLoginPage = false; | ||
| let menuItems = [ | ||
| { title: 'ประกาศ', href: '/admin' }, | ||
| { title: 'เอกสาร', href: '/admin/documents' }, | ||
| { title: 'สถิติ และ งบประมาณ', href: '/admin/stats' }, | ||
| { title: 'จัดการผู้ใช้งาน', href: '/admin/users' } | ||
| ]; | ||
|
|
||
| let user = { | ||
| name: 'Not found', | ||
| lastname: 'Not found', | ||
| role: 'Not found', | ||
| id: 'Not found' | ||
| }; | ||
|
|
||
| onMount(async () => { | ||
| const pathname = window.location.pathname; | ||
| isAdminPage = pathname.startsWith('/admin'); | ||
| isAuthLoginPage = pathname === '/auth/login'; | ||
|
|
||
| if (isAdminPage) { | ||
| const token = getDecryptedToken(); | ||
| if (token) { | ||
| try { | ||
| const userData = await getMe(token); | ||
| user = { | ||
| name: userData.result.first_name, | ||
| lastname: userData.result.last_name, | ||
| role: userData.result.role, | ||
| id: userData.result.id | ||
| }; | ||
| } catch (error) { | ||
| console.error('Failed to fetch user data:', error); | ||
| } | ||
| } else { | ||
| console.log('No token found'); | ||
| window.location.replace('/auth/login'); | ||
| } | ||
| } | ||
| }); | ||
| </script> | ||
|
|
||
| <div> | ||
| <Navbar /> | ||
| <slot /> | ||
| <Footer /> | ||
| </div> | ||
| {#if isAdminPage} | ||
| <div class="min-h-screen bg-white z-50 w-full"> | ||
| <Sidebar {menuItems} {user} /> | ||
| <slot /> | ||
| </div> | ||
| {:else if isAuthLoginPage} | ||
| <div> | ||
| <slot /> | ||
| </div> | ||
| {:else} | ||
| <div> | ||
| <Navbar /> | ||
| <slot /> | ||
| <Footer /> | ||
| </div> | ||
| {/if} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,194 @@ | ||
| <script lang="ts"> | ||
| import SearchBar from '$lib/components/SearchBar.svelte'; | ||
| import Button from '$lib/components/Button.svelte'; | ||
| import Pagination from '$lib/components/Pagination/Pagination.svelte'; | ||
| import { onMount } from 'svelte'; | ||
|
|
||
| export let title: string = 'จัดการผู้ใช้งาน'; | ||
| export let subtitle: string = 'ผู้ใช้งานที่มีสิทธิ์เข้าถึงการแก้ไขและเปลี่ยนแปลงเอกสารทั้งหมด'; | ||
|
|
||
| let searchQuery = ''; | ||
| let activePopoverId: string | null = null; | ||
|
|
||
| let users = [ | ||
| { id: '6753736321', title: 'นางสาวหนูเต็งก เต็งเต็งเต็ง', date: '01/11/2024', role: 'Admin' }, | ||
| { id: '6753736322', title: 'นางสาวหนูเต็งก เต็งเต็งเต็ง', date: '01/11/2024', role: 'Admin' }, | ||
| { id: '6753736323', title: 'นางสาวหนูเต็งข เต็งเต็งเต็ง', date: '01/11/2024', role: 'Admin' }, | ||
| { id: '6753736324', title: 'นางสาวหนูเต็งข เต็งเต็งเต็ง', date: '01/11/2024', role: 'Admin' }, | ||
| { id: '6753736325', title: 'นางสาวหนูเต็ง เต็งเต็งเต็ง', date: '01/11/2024', role: 'Admin' }, | ||
| { id: '6753736326', title: 'นางสาวหนูเต็ง เต็งเต็งเต็ง', date: '01/11/2024', role: 'Admin' }, | ||
| { id: '6753736327', title: 'นางสาวหนูเต็ง เต็งเต็งเต็ง', date: '01/11/2024', role: 'Admin' }, | ||
| { id: '6753736328', title: 'นางสาวหนูเต็ง เต็งเต็งเต็ง', date: '01/11/2024', role: 'Admin' }, | ||
| { id: '6753736329', title: 'นางสาวหนูเต็ง เต็งเต็งเต็ง', date: '01/11/2024', role: 'Admin' }, | ||
| { id: '6753736320', title: 'นางสาวหนูเต็ง เต็งเต็งเต็ง', date: '01/11/2024', role: 'Admin' } | ||
| ]; | ||
|
|
||
| type User = { | ||
| id: string; | ||
| title: string; | ||
| date: string; | ||
| role: string; | ||
| }; | ||
|
|
||
| let filteredUsers: User[] = []; | ||
| let selectedItems: string[] = []; | ||
| let currentPage = 1; | ||
| let itemsPerPage = 5; | ||
| let paginatedIds: string[] = []; | ||
|
|
||
| function togglePopover(id: string) { | ||
| activePopoverId = activePopoverId === id ? null : id; | ||
| } | ||
|
|
||
| function handleClickOutside(event: MouseEvent) { | ||
| const target = event.target as HTMLElement; | ||
| if (!target.closest('.popover') && !target.closest('.more-options-button')) { | ||
| activePopoverId = null; | ||
| } | ||
| } | ||
|
|
||
| function updateFilteredUsers() { | ||
| const startIndex = (currentPage - 1) * itemsPerPage; | ||
| const endIndex = startIndex + itemsPerPage; | ||
| filteredUsers = users | ||
| .filter( | ||
| (user) => | ||
| user.title.toLowerCase().includes(searchQuery.toLowerCase()) || | ||
| user.id.includes(searchQuery) || | ||
| user.role.toLowerCase().includes(searchQuery.toLowerCase()) | ||
| ) | ||
| .filter((user) => paginatedIds.includes(user.id)) | ||
| .slice(startIndex, endIndex); | ||
| } | ||
|
|
||
| $: { | ||
| updateFilteredUsers(); | ||
| } | ||
|
|
||
| function toggleSelectAll(event: Event) { | ||
| const checkbox = event.target as HTMLInputElement; | ||
| if (checkbox.checked) { | ||
| selectedItems = filteredUsers.map((user) => user.id); | ||
| } else { | ||
| selectedItems = []; | ||
| } | ||
| } | ||
|
|
||
| function toggleSelectItem(id: string) { | ||
| const index = selectedItems.indexOf(id); | ||
| if (index === -1) { | ||
| selectedItems = [...selectedItems, id]; | ||
| } else { | ||
| selectedItems = selectedItems.filter((item) => item !== id); | ||
| } | ||
| } | ||
|
|
||
| function handlePageChange(event: CustomEvent) { | ||
| paginatedIds = event.detail.paginatedItems; | ||
| itemsPerPage = parseInt(event.detail.itemsPerPage); | ||
| updateFilteredUsers(); | ||
| } | ||
|
|
||
| onMount(() => { | ||
| document.addEventListener('click', handleClickOutside); | ||
| return () => { | ||
| document.removeEventListener('click', handleClickOutside); | ||
| }; | ||
| }); | ||
| </script> | ||
|
|
||
| <svelte:window on:click={handleClickOutside} /> | ||
|
|
||
| <div class="pl-64 min-h-screen bg-white p-8"> | ||
| <div class="mb-4 mt-5"> | ||
| <h1 class="text-2xl font-bold text-gray-800">{title}</h1> | ||
| <p class="text-sm text-gray-600">{subtitle}</p> | ||
| </div> | ||
|
|
||
| <div class="mb-4"> | ||
| <SearchBar bind:value={searchQuery} /> | ||
| </div> | ||
|
|
||
| <div class="rounded-lg"> | ||
| <!-- Header --> | ||
| <div class="flex items-center border-b border-sucu-pink-02 px-4 py-3 mb-2"> | ||
| <div class="flex w-12 items-center"> | ||
| <input | ||
| type="checkbox" | ||
| on:change={toggleSelectAll} | ||
| checked={selectedItems.length === filteredUsers.length} | ||
| class="h-4 w-4 rounded border-gray-300" | ||
| /> | ||
| </div> | ||
| <div class="flex-1 font-semibold text-sm">เลือกทั้งหมด</div> | ||
| <div class="w-32"></div> | ||
| <div class="w-40"></div> | ||
| <div class="w-12"></div> | ||
|
|
||
| <Button | ||
| class="w-24 h-8 shadow-none font-normal text-sm" | ||
| on:click={() => console.log('Button clicked')} | ||
| > | ||
| เพิ่มผู้ใช้งาน | ||
| </Button> | ||
| </div> | ||
|
|
||
| <!-- List Items --> | ||
| {#each filteredUsers as user} | ||
| <div class="flex items-center px-4 py-3 hover:bg-gray-50 shadow-md my-2 relative"> | ||
| <div class="flex w-12 items-center"> | ||
| <input | ||
| type="checkbox" | ||
| checked={selectedItems.includes(user.id)} | ||
| on:change={() => toggleSelectItem(user.id)} | ||
| class="h-4 w-4 rounded border-gray-300" | ||
| /> | ||
| </div> | ||
| <div class="flex-1 font-medium text-gray-900">{user.title}</div> | ||
| <div class="w-32 text-gray-600">{user.id}</div> | ||
| <div class="w-40 text-gray-600">สร้างเมื่อ {user.date}</div> | ||
| <div class="w-24"> | ||
| <span class="rounded-full bg-pink-100 px-3 py-1 text-sm font-medium text-pink-800"> | ||
| {user.role} | ||
| </span> | ||
| </div> | ||
| <div class="w-12 relative"> | ||
| <button | ||
| class="text-gray-500 hover:text-gray-700 more-options-button" | ||
| aria-label="More options" | ||
| on:click|stopPropagation={() => togglePopover(user.id)} | ||
| > | ||
| <svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"> | ||
| <path | ||
| d="M10 6a2 2 0 110-4 2 2 0 010 4zM10 12a2 2 0 110-4 2 2 0 010 4zM10 18a2 2 0 110-4 2 2 0 010 4z" | ||
| /> | ||
| </svg> | ||
| </button> | ||
| {#if activePopoverId === user.id} | ||
| <div class="absolute right-0 mt-2 w-32 rounded-lg bg-white shadow-lg z-50 popover"> | ||
| <div class="py-1"> | ||
| <button | ||
| class="block w-full px-4 py-2 text-sm text-left text-gray-700 hover:bg-gray-100" | ||
| on:click={() => console.log('Edit user')} | ||
| > | ||
| แก้ไข | ||
| </button> | ||
| <button | ||
| class="block w-full px-4 py-2 text-sm text-left text-gray-700 hover:bg-gray-100" | ||
| on:click={() => console.log('Remove user')} | ||
| > | ||
| ลบ | ||
| </button> | ||
| </div> | ||
| </div> | ||
| {/if} | ||
| </div> | ||
| </div> | ||
| {/each} | ||
|
|
||
| <!-- Pagination --> | ||
| <div class="mt-4 w-full flex justify-end"> | ||
| <Pagination Arrayitem={users.map((user) => user.id)} on:pageChange={handlePageChange} /> | ||
| </div> | ||
| </div> | ||
| </div> |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
อันนี้เอาไปใส่
.envและ.env.exampleให้เป็นVITE_API_URLได้ไหมนะ