Commit 9d293bda by tungnq

TODO: Đã cấu hình xong cho android đã chạy thử nghiệm test thành công

parent 6efbfc0e
module.exports = { module.exports = {
presets: ['module:@react-native/babel-preset'], presets: ['module:@react-native/babel-preset'],
plugins: ['react-native-reanimated/plugin'],
}; };
// metro.config.js - FIXED: Define defaultConfig trước khi sử dụng
const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config'); const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
/** /**
...@@ -6,6 +7,24 @@ const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config'); ...@@ -6,6 +7,24 @@ const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
* *
* @type {import('metro-config').MetroConfig} * @type {import('metro-config').MetroConfig}
*/ */
const config = {};
module.exports = mergeConfig(getDefaultConfig(__dirname), config); // FIXME: Phải define defaultConfig TRƯỚC khi sử dụng trong config
const defaultConfig = getDefaultConfig(__dirname);
const config = {
// FEATURE: Cấu hình transformer để xử lý file SVG
transformer: {
babelTransformerPath: require.resolve('react-native-svg-transformer'),
},
// FEATURE: Cấu hình resolver để nhận diện file SVG
resolver: {
// NOTE: Loại bỏ 'svg' khỏi assetExts để không treat như asset
assetExts: defaultConfig.resolver.assetExts.filter(ext => ext !== 'svg'),
// NOTE: Thêm 'svg' vào sourceExts để treat như source code
sourceExts: [...defaultConfig.resolver.sourceExts, 'svg'],
},
};
module.exports = mergeConfig(defaultConfig, config);
\ No newline at end of file
...@@ -11,12 +11,12 @@ ...@@ -11,12 +11,12 @@
}, },
"dependencies": { "dependencies": {
"@react-native-community/async-storage": "^1.12.1", "@react-native-community/async-storage": "^1.12.1",
"@react-native-community/checkbox": "^0.5.9",
"@react-native-community/masked-view": "^0.1.11", "@react-native-community/masked-view": "^0.1.11",
"@react-native-community/netinfo": "^7.1.7", "@react-native-community/netinfo": "^7.1.7",
"@react-native-community/picker": "^1.8.1", "@react-native-community/picker": "^1.8.1",
"@react-native-community/slider": "^4.1.12", "@react-native-community/slider": "^4.1.12",
"@react-navigation/bottom-tabs": "^6.0.9", "@react-navigation/bottom-tabs": "^6.0.9",
"@react-navigation/drawer": "^6.x",
"@react-navigation/material-top-tabs": "^6.0.6", "@react-navigation/material-top-tabs": "^6.0.6",
"@react-navigation/native": "^6.0.6", "@react-navigation/native": "^6.0.6",
"@react-navigation/stack": "^6.0.11", "@react-navigation/stack": "^6.0.11",
...@@ -49,7 +49,8 @@ ...@@ -49,7 +49,8 @@
"react-native-permissions": "^3.6.1", "react-native-permissions": "^3.6.1",
"react-native-progress": "^5.0.0", "react-native-progress": "^5.0.0",
"react-native-qrcode-scanner": "^1.5.5", "react-native-qrcode-scanner": "^1.5.5",
"react-native-reanimated": "^3.12.1", "react-native-reanimated-table": "^0.0.2",
"react-native-reanimated": "3.12.1",
"react-native-rename": "^2.9.0", "react-native-rename": "^2.9.0",
"react-native-render-html": "^6.1.0", "react-native-render-html": "^6.1.0",
"react-native-responsive-fontsize": "^0.5.1", "react-native-responsive-fontsize": "^0.5.1",
...@@ -85,6 +86,7 @@ ...@@ -85,6 +86,7 @@
"eslint": "^8.19.0", "eslint": "^8.19.0",
"jest": "^29.6.3", "jest": "^29.6.3",
"prettier": "2.8.8", "prettier": "2.8.8",
"react-native-svg-transformer": "^1.5.1",
"react-test-renderer": "18.2.0", "react-test-renderer": "18.2.0",
"typescript": "5.0.4" "typescript": "5.0.4"
}, },
......
...@@ -2,7 +2,7 @@ import React, {useEffect, useRef, useState} from 'react'; ...@@ -2,7 +2,7 @@ import React, {useEffect, useRef, useState} from 'react';
import StackNavigation from './routers/StackNavigation'; import StackNavigation from './routers/StackNavigation';
import {connect} from 'react-redux'; import {connect} from 'react-redux';
import DropdownAlert from 'react-native-dropdownalert'; import DropdownAlert from 'react-native-dropdownalert';
import DropdownManager from './components/DropdownAlert/DropdownManager'; import DropdownManager from './components/Dropdown/DropdownAlert/DropdownManager';
import R from './assets/R'; import R from './assets/R';
import {HEIGHTXD, WIDTHXD} from './config/Functions'; import {HEIGHTXD, WIDTHXD} from './config/Functions';
import {SkypeIndicator} from 'react-native-indicators'; import {SkypeIndicator} from 'react-native-indicators';
......
const colors = { import { green } from "react-native-reanimated/lib/typescript/reanimated2/Colors";
main: '#00139B',
txtMain: '#006938',
label: '#5D5D5D',
color777: '#777777',
colorMainLight: '#5173B1', const colors = {
colorBackground: '#E2E8F2', main: '#2F6BFF',
borderGray: '#bbc2ce', txtMain: '#000000',
borderGraym: '#c9cfd9', shadowColor:'rgba(0, 0, 0, 0.25)',
placeHolder: '#8D8D8D', label: 'rgba(75, 75, 75, 0.8)',
backgrPicker: '#E2E8F2', iconColorSel:'#2F6BFF',
dollar: '#07BA00', iconColorUnSel:'#6C6C6C',
buttonColorUnSel:'#C4C4C4',
buttonColorSel:'rgba(47, 107, 255, 0.79)',
blue100:'rgba(47, 107, 255, 0.1)',
orange: '#F39A2B',
red:'#D60A0B',
textSubMain:'#2F6BFF',
backgroundInputSearch:'rgba(0, 0, 0, 0.13)',
grey:'rgba(204, 204, 204, 0.5)',
buttonHome:'rgba(217, 217, 217, 0.56)',
black:'#000000',
black_800:'rgba(22, 21, 28, 0.8)',
backgroundCard:'rgba(47, 107, 255, 0.1)',
grey_200:'rgba(162, 161, 168, 0.2)',
grey_50:'rgba(162, 161, 168, 0.05)',
grey_100:'rgba(162, 161, 168, 1)',
grey_800:'rgba(75, 75, 75, 0.8)',
yellow:'#F39A2B',
black_500:'rgba(0, 0, 0, 0.5)',
purple:'rgba(158, 183, 247, 1)',
colorBg: '#E8E8E8',
accent: '#A60014',
primary: '#0AC4BA',
secondary: '#00b33c',
black: '#000000',
white: '#FFFFFF',
orange: '#Fb9736',
lightBlue: '#1a8cff',
lightBlue1: '#008ae6',
lightBlue2: '#22AEFB',
rgbaBtn: '#79F8B5', // 🔵 Blue Shades
yellow: '#e6e600', blue500: '#2F6BFF', // main, iconColorSel, textSubMain
red: '#E3434F', blue400: 'rgba(47, 107, 255, 0.79)', // buttonColorSel
bgMain: '#9EF8C9', blue100: 'rgba(47, 107, 255, 0.1)', // blue100, backgroundCard
colorButton1: '#43D75B',
colorTextLine: '#FFA412', // ⚫ Black Shades
colorBgScreen: '#F7F8FA', black900: '#000000', // txtMain, black
colorBgInputText: '#F2F2F2', black800: 'rgba(22, 21, 28, 0.8)', // black_800
black500: 'rgba(0, 0, 0, 0.5)', // black_500
black250: 'rgba(0, 0, 0, 0.25)', // shadowColor
// ⚪ White
white: '#ffffff',
// ⚪ Gray Shades
gray280: 'rgba(204, 204, 204, 0.36)',
gray220: 'rgba(217, 217, 217, 0.56)',
gray800: 'rgba(75, 75, 75, 0.8)', // label, grey_800
gray600: '#6C6C6C', // iconColorUnSel
gray400: '#C4C4C4', // buttonColorUnSel
gray300: 'rgba(204, 204, 204, 0.5)', // grey
gray200: 'rgba(162, 161, 168, 1)', // grey_100
gray150: 'rgba(162, 161, 168, 0.2)', // grey_200
gray50: 'rgba(162, 161, 168, 0.05)', // grey_50
greyBlue_soft_50: 'rgba(221, 222, 238, 0.5)',
// 🟢 Green
green500: 'rgba(0, 255, 123, 1)', // green
green:'rgba(0, 255, 21, 1)',
// 🟠 Orange/Yellow
orange500: '#F39A2B', // orange, yellow (trùng mã)
// 🔴 Red
red500: '#D60A0B', // red
// 🟣 Purple
purple500: 'rgba(158, 183, 247, 1)', // purple
// 🔘 Background / UI
inputBackground: 'rgba(0, 0, 0, 0.13)', // backgroundInputSearch
buttonBackground: 'rgba(217, 217, 217, 0.56)', // buttonHome
}; };
export default colors; export default colors;
const fonts = { const fonts = {
RobotoThin: 'Roboto-Thin', InterMedium:'Inter_18pt-Medium',
RobotoThinItalic: 'Roboto-ThinItalic', InterRegular:'Inter_18pt-Regular',
RobotoItalic: 'Roboto-Italic', InterSemiBold:'Inter_18pt-SemiBold',
RobotoBlack: 'Roboto-Black',
RobotoBlackItalic: 'Roboto-BlackItalic',
RobotoBold: 'Roboto-Bold',
RobotoBoldItalic: 'Roboto-BoldItalic',
RobotoLight: 'Roboto-Light',
RobotoLightItalic: 'Roboto-LightItalic',
RobotoMedium: 'Roboto-Medium',
RobotoMediumItalic: 'Roboto-MediumItalic',
RobotoRegular: 'Roboto-Regular',
MontserratRegular: 'Montserrat-Regular',
MontserratMedium: 'Montserrat-Medium',
MontserratSemiBold: 'Montserrat-SemiBold',
MontserratItalic: 'Montserrat-Italic',
MontserratMediumItalic: 'Montserrat-MediumItalic',
MontserratSemiBoldItalic: 'Montserrat-SemiBoldItalic',
}; };
export default fonts; export default fonts;
const fontsize = { const fontsize = {
fontSizeLabel: 14, fontsSize8:8,
fontSizeContent: 14, fontsSize10:10,
fontSizeInputText: 16, fontsSize12: 12,
fontsSize14:14,
fontsSize16:16,
fontsSize18:18,
fontsSize20:20,
}; };
export default fontsize; export default fontsize;
<svg width="12" height="6" viewBox="0 0 12 6" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 1L6 5L11 1" stroke="#16151C" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<svg width="13" height="22" viewBox="0 0 13 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.8686 12.207L2.37338 21.865L0 19.4509L8.3085 11L0 2.54907L2.37338 0.13501L11.8686 9.79298C12.1832 10.1131 12.36 10.5473 12.36 11C12.36 11.4527 12.1832 11.8869 11.8686 12.207Z" fill="black"/>
</svg>
<svg width="20" height="18" viewBox="0 0 20 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.67456 0.329502C10.1084 0.768849 10.1084 1.48115 9.67456 1.9205L3.79357 7.87501H18.8889C19.5026 7.87501 20 8.37868 20 9.00001C20 9.62135 19.5026 10.125 18.8889 10.125H3.79357L9.67456 16.0795C10.1084 16.5189 10.1084 17.2312 9.67456 17.6705C9.24067 18.1098 8.53711 18.1098 8.10322 17.6705L0.325433 9.7955C0.117067 9.58457 0 9.29836 0 9.00001C0 8.70166 0.117067 8.41546 0.325433 8.20453L8.10322 0.329502C8.53711 -0.109834 9.24067 -0.109834 9.67456 0.329502Z" fill="white"/>
</svg>
<svg width="20" height="18" viewBox="0 0 20 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.67456 0.329502C10.1084 0.768849 10.1084 1.48115 9.67456 1.9205L3.79357 7.87501H18.8889C19.5026 7.87501 20 8.37868 20 9.00001C20 9.62135 19.5026 10.125 18.8889 10.125H3.79357L9.67456 16.0795C10.1084 16.5189 10.1084 17.2312 9.67456 17.6705C9.24067 18.1098 8.53711 18.1098 8.10322 17.6705L0.325433 9.7955C0.117067 9.58457 0 9.29836 0 9.00001C0 8.70166 0.117067 8.41546 0.325433 8.20453L8.10322 0.329502C8.53711 -0.109834 9.24067 -0.109834 9.67456 0.329502Z" fill="black"/>
</svg>
<svg width="38" height="38" viewBox="0 0 38 38" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1.5 13.1667H36.5M9.27778 1.5V5.38889M28.7222 1.5V5.38889M7.33333 19H11.2222M17.0556 19H20.9444M26.7778 19H30.6667M7.33333 24.8333H11.2222M17.0556 24.8333H20.9444M26.7778 24.8333H30.6667M7.33333 30.6667H11.2222M17.0556 30.6667H20.9444M26.7778 30.6667H30.6667M7.72222 36.5H30.2778C32.4557 36.5 33.5448 36.5 34.3767 36.0761C35.1084 35.7034 35.7034 35.1084 36.0761 34.3767C36.5 33.5448 36.5 32.4557 36.5 30.2778V11.6111C36.5 9.43312 36.5 8.34413 36.0761 7.51226C35.7034 6.78051 35.1084 6.18559 34.3767 5.81276C33.5448 5.38889 32.4557 5.38889 30.2778 5.38889H7.72222C5.54425 5.38889 4.45524 5.38889 3.62337 5.81276C2.89162 6.18559 2.2967 6.78051 1.92387 7.51226C1.5 8.34413 1.5 9.43312 1.5 11.6111V30.2778C1.5 32.4557 1.5 33.5448 1.92387 34.3767C2.2967 35.1084 2.89162 35.7034 3.62337 36.0761C4.45524 36.5 5.54423 36.5 7.72222 36.5Z" stroke="#2F6BFF" stroke-width="2" stroke-linecap="round"/>
</svg>
<svg width="26" height="29" viewBox="0 0 26 29" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.9964 16.4115C15.9964 18.3275 14.5748 19.8807 12.8212 19.8807C11.0676 19.8807 9.646 18.3275 9.646 16.4115C9.646 14.4955 11.0676 12.9423 12.8212 12.9423C14.5748 12.9423 15.9964 14.4955 15.9964 16.4115Z" stroke="#16151C" stroke-width="1.5"/>
<ellipse cx="12.8216" cy="8.89493" rx="1.0584" ry="1.1564" fill="#16151C"/>
<path d="M22.3471 18.7243V14.0987C22.3471 10.9054 19.9778 8.31672 17.0551 8.31672H16.9217C16.4517 6.32167 14.7942 4.84752 12.8215 4.84752C10.8488 4.84752 9.19125 6.32167 8.72128 8.31672H8.5879C5.66521 8.31672 3.2959 10.9054 3.2959 14.0987V18.7243C3.2959 21.9176 5.66521 24.5063 8.5879 24.5063H17.0551C19.9778 24.5063 22.3471 21.9176 22.3471 18.7243Z" stroke="#16151C" stroke-width="1.5" stroke-linejoin="round"/>
</svg>
<svg width="38" height="38" viewBox="0 0 38 38" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1.5 13.1667H36.5M9.27778 1.5V5.38889M28.7222 1.5V5.38889M7.33333 19H11.2222M17.0556 19H20.9444M26.7778 19H30.6667M7.33333 24.8333H11.2222M17.0556 24.8333H20.9444M26.7778 24.8333H30.6667M7.33333 30.6667H11.2222M17.0556 30.6667H20.9444M26.7778 30.6667H30.6667M7.72222 36.5H30.2778C32.4557 36.5 33.5448 36.5 34.3767 36.0761C35.1084 35.7034 35.7034 35.1084 36.0761 34.3767C36.5 33.5448 36.5 32.4557 36.5 30.2778V11.6111C36.5 9.43312 36.5 8.34413 36.0761 7.51226C35.7034 6.78051 35.1084 6.18559 34.3767 5.81276C33.5448 5.38889 32.4557 5.38889 30.2778 5.38889H7.72222C5.54425 5.38889 4.45524 5.38889 3.62337 5.81276C2.89162 6.18559 2.2967 6.78051 1.92387 7.51226C1.5 8.34413 1.5 9.43312 1.5 11.6111V30.2778C1.5 32.4557 1.5 33.5448 1.92387 34.3767C2.2967 35.1084 2.89162 35.7034 3.62337 36.0761C4.45524 36.5 5.54423 36.5 7.72222 36.5Z" stroke="none" stroke-width="2" stroke-linecap="round"/>
</svg>
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7 1.5V18.5M18.5 7.5H1.5M1 7.4C1 5.16 1 4.04 1.436 3.184C1.81949 2.43139 2.43139 1.81949 3.184 1.436C4.04 1 5.16 1 7.4 1H12.6C14.84 1 15.96 1 16.816 1.436C17.5686 1.81949 18.1805 2.43139 18.564 3.184C19 4.04 19 5.16 19 7.4V12.6C19 14.84 19 15.96 18.564 16.816C18.1805 17.5686 17.5686 18.1805 16.816 18.564C15.96 19 14.84 19 12.6 19H7.4C5.16 19 4.04 19 3.184 18.564C2.43139 18.1805 1.81949 17.5686 1.436 16.816C1 15.96 1 14.84 1 12.6V7.4Z" stroke="none" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<svg width="40" height="42" viewBox="0 0 40 42" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="path-1-inside-1_3567_598" fill="white">
<path d="M20.0659 33.2717C19.8019 32.6917 19.2553 32.2685 18.5937 32.1933C18.0334 32.1293 17.4849 32.0093 16.9633 31.8363C16.7294 31.7576 16.5099 31.6717 16.3067 31.5797L16.6443 30.6595C16.8827 30.7659 17.1283 30.8632 17.3798 30.9512L17.3902 30.9549L17.4009 30.9584C18.2078 31.2251 19.0019 31.3603 19.7606 31.3603C20.5225 31.3603 21.0747 31.2581 21.499 31.0392L21.5243 31.0261L21.5491 31.0117C22.1918 30.6429 22.5606 30.0091 22.5606 29.2723C22.5606 28.4547 22.1195 27.776 21.3502 27.4093C20.9633 27.2211 20.4137 27.0413 19.6219 26.8445C18.8099 26.6392 18.1278 26.4213 17.5987 26.1981C17.2121 26.0232 16.8766 25.744 16.5742 25.3448C16.3139 24.9885 16.1822 24.4528 16.1822 23.7517C16.1822 22.9059 16.4203 22.2077 16.9105 21.6173C17.3467 21.1 17.9662 20.7485 18.7977 20.5515C19.4145 20.4053 19.8955 19.9651 20.1118 19.3965C20.3563 19.9728 20.8766 20.4013 21.5241 20.5045C22.0649 20.5907 22.5614 20.728 23.0078 20.9139L22.6971 21.8157C21.8542 21.4773 21.0246 21.3064 20.2222 21.3064C17.5574 21.3064 17.4225 23.1448 17.4225 23.5133C17.4225 24.3123 17.8539 24.9821 18.6062 25.3515L18.6273 25.3619L18.6486 25.3715C19.0286 25.5411 19.5678 25.7045 20.3459 25.8859C21.1505 26.0677 21.8214 26.276 22.3486 26.5069L22.3595 26.5117L22.3705 26.5163C22.7619 26.6792 23.0977 26.9512 23.3969 27.348L23.4017 27.3541L23.4065 27.3605C23.6681 27.6987 23.8009 28.2216 23.8009 28.9149C23.8009 29.7152 23.5691 30.3925 23.0923 30.9859C22.6787 31.5005 22.1014 31.8568 21.3267 32.0749C20.7286 32.2435 20.2646 32.7008 20.0659 33.2717Z"/>
</mask>
<path d="M20.0659 33.2717C19.8019 32.6917 19.2553 32.2685 18.5937 32.1933C18.0334 32.1293 17.4849 32.0093 16.9633 31.8363C16.7294 31.7576 16.5099 31.6717 16.3067 31.5797L16.6443 30.6595C16.8827 30.7659 17.1283 30.8632 17.3798 30.9512L17.3902 30.9549L17.4009 30.9584C18.2078 31.2251 19.0019 31.3603 19.7606 31.3603C20.5225 31.3603 21.0747 31.2581 21.499 31.0392L21.5243 31.0261L21.5491 31.0117C22.1918 30.6429 22.5606 30.0091 22.5606 29.2723C22.5606 28.4547 22.1195 27.776 21.3502 27.4093C20.9633 27.2211 20.4137 27.0413 19.6219 26.8445C18.8099 26.6392 18.1278 26.4213 17.5987 26.1981C17.2121 26.0232 16.8766 25.744 16.5742 25.3448C16.3139 24.9885 16.1822 24.4528 16.1822 23.7517C16.1822 22.9059 16.4203 22.2077 16.9105 21.6173C17.3467 21.1 17.9662 20.7485 18.7977 20.5515C19.4145 20.4053 19.8955 19.9651 20.1118 19.3965C20.3563 19.9728 20.8766 20.4013 21.5241 20.5045C22.0649 20.5907 22.5614 20.728 23.0078 20.9139L22.6971 21.8157C21.8542 21.4773 21.0246 21.3064 20.2222 21.3064C17.5574 21.3064 17.4225 23.1448 17.4225 23.5133C17.4225 24.3123 17.8539 24.9821 18.6062 25.3515L18.6273 25.3619L18.6486 25.3715C19.0286 25.5411 19.5678 25.7045 20.3459 25.8859C21.1505 26.0677 21.8214 26.276 22.3486 26.5069L22.3595 26.5117L22.3705 26.5163C22.7619 26.6792 23.0977 26.9512 23.3969 27.348L23.4017 27.3541L23.4065 27.3605C23.6681 27.6987 23.8009 28.2216 23.8009 28.9149C23.8009 29.7152 23.5691 30.3925 23.0923 30.9859C22.6787 31.5005 22.1014 31.8568 21.3267 32.0749C20.7286 32.2435 20.2646 32.7008 20.0659 33.2717Z" fill="black"/>
<path d="M20.0659 33.2717L18.2456 34.1003L20.314 38.6445L21.9548 33.929L20.0659 33.2717ZM18.5937 32.1933L18.3667 34.1804L18.3678 34.1806L18.5937 32.1933ZM16.9633 31.8363L16.3256 33.7319L16.3334 33.7345L16.9633 31.8363ZM16.3067 31.5797L14.4291 30.8909L13.7891 32.6353L15.4818 33.4017L16.3067 31.5797ZM16.6443 30.6595L17.4594 28.8331L15.5041 27.9604L14.7667 29.9707L16.6443 30.6595ZM17.3798 30.9512L18.0554 29.0688L18.0479 29.0661L18.0404 29.0635L17.3798 30.9512ZM17.3902 30.9549L16.7145 32.8374L16.7433 32.8477L16.7724 32.8572L17.3902 30.9549ZM17.4009 30.9584L18.0284 29.0594L18.0186 29.0562L17.4009 30.9584ZM21.499 31.0392L20.5821 29.2617L20.5818 29.2619L21.499 31.0392ZM21.5243 31.0261L22.4412 32.8036L22.4854 32.7808L22.5285 32.7558L21.5243 31.0261ZM21.5491 31.0117L20.5537 29.2771L20.5449 29.2821L21.5491 31.0117ZM21.3502 27.4093L20.4751 29.2078L20.4824 29.2113L20.4897 29.2148L21.3502 27.4093ZM19.6219 26.8445L19.1316 28.7835L19.1395 28.7855L19.6219 26.8445ZM17.5987 26.1981L16.7743 28.0203L16.7977 28.0309L16.8213 28.0409L17.5987 26.1981ZM16.5742 25.3448L14.9592 26.5246L14.9695 26.5386L14.98 26.5525L16.5742 25.3448ZM16.9105 21.6173L15.3815 20.328L15.3766 20.3339L15.3716 20.3399L16.9105 21.6173ZM18.7977 20.5515L18.3366 18.6054L18.3364 18.6054L18.7977 20.5515ZM20.1118 19.3965L21.9529 18.6153L20.0104 14.0377L18.2425 18.6855L20.1118 19.3965ZM21.5241 20.5045L21.2092 22.4796L21.2095 22.4797L21.5241 20.5045ZM23.0078 20.9139L24.8987 21.5653L25.5105 19.7895L23.7766 19.0675L23.0078 20.9139ZM22.6971 21.8157L21.952 23.6718L23.9033 24.4551L24.5881 22.4671L22.6971 21.8157ZM18.6062 25.3515L19.4916 23.5581L19.4876 23.5562L18.6062 25.3515ZM18.6273 25.3619L17.7418 27.1552L17.7739 27.171L17.8065 27.1857L18.6273 25.3619ZM18.6486 25.3715L17.8279 27.1953L17.8335 27.1978L18.6486 25.3715ZM20.3459 25.8859L19.892 27.8337L19.9049 27.8367L20.3459 25.8859ZM22.3486 26.5069L23.1528 24.6758L23.1511 24.675L22.3486 26.5069ZM22.3595 26.5117L21.5553 28.3429L21.5744 28.3513L21.5937 28.3593L22.3595 26.5117ZM22.3705 26.5163L23.139 24.6698L23.1363 24.6687L22.3705 26.5163ZM23.3969 27.348L21.8 28.5521L21.8108 28.5665L21.822 28.5808L23.3969 27.348ZM23.4017 27.3541L25.0017 26.1541L24.9893 26.1376L24.9766 26.1214L23.4017 27.3541ZM23.4065 27.3605L21.8065 28.5605L21.8154 28.5725L21.8246 28.5844L23.4065 27.3605ZM23.0923 30.9859L24.6513 32.2387L24.6513 32.2387L23.0923 30.9859ZM21.3267 32.0749L20.7846 30.1498L20.7843 30.1499L21.3267 32.0749ZM20.0659 33.2717L21.8862 32.4432C21.3454 31.2549 20.2126 30.3645 18.8195 30.2061L18.5937 32.1933L18.3678 34.1806C18.3374 34.1771 18.3075 34.1645 18.2853 34.148C18.2652 34.1331 18.2529 34.1163 18.2456 34.1003L20.0659 33.2717ZM18.5937 32.1933L18.8206 30.2063C18.3933 30.1575 17.98 30.0664 17.5931 29.938L16.9633 31.8363L16.3334 33.7345C16.9897 33.9523 17.6735 34.1012 18.3667 34.1804L18.5937 32.1933ZM16.9633 31.8363L17.6009 29.9407C17.425 29.8815 17.2684 29.8197 17.1316 29.7578L16.3067 31.5797L15.4818 33.4017C15.7514 33.5238 16.0338 33.6338 16.3256 33.7319L16.9633 31.8363ZM16.3067 31.5797L18.1844 32.2686L18.522 31.3483L16.6443 30.6595L14.7667 29.9707L14.4291 30.8909L16.3067 31.5797ZM16.6443 30.6595L15.8292 32.4858C16.1198 32.6155 16.417 32.7332 16.7192 32.839L17.3798 30.9512L18.0404 29.0635C17.8396 28.9932 17.6457 28.9162 17.4594 28.8331L16.6443 30.6595ZM17.3798 30.9512L16.7041 32.8336L16.7145 32.8374L17.3902 30.9549L18.0658 29.0725L18.0554 29.0688L17.3798 30.9512ZM17.3902 30.9549L16.7724 32.8572L16.7831 32.8606L17.4009 30.9584L18.0186 29.0562L18.0079 29.0527L17.3902 30.9549ZM17.4009 30.9584L16.7733 32.8574C17.7587 33.1831 18.7648 33.3603 19.7606 33.3603V31.3603V29.3603C19.239 29.3603 18.6568 29.2671 18.0284 29.0594L17.4009 30.9584ZM19.7606 31.3603V33.3603C20.6719 33.3603 21.5885 33.2436 22.4161 32.8165L21.499 31.0392L20.5818 29.2619C20.561 29.2727 20.373 29.3603 19.7606 29.3603V31.3603ZM21.499 31.0392L22.4158 32.8167L22.4412 32.8036L21.5243 31.0261L20.6075 29.2487L20.5821 29.2617L21.499 31.0392ZM21.5243 31.0261L22.5285 32.7558L22.5533 32.7414L21.5491 31.0117L20.5449 29.2821L20.5201 29.2965L21.5243 31.0261ZM21.5491 31.0117L22.5446 32.7464C23.8037 32.0239 24.5606 30.7312 24.5606 29.2723H22.5606H20.5606C20.5606 29.289 20.5586 29.2948 20.5593 29.292C20.5596 29.2908 20.5603 29.2884 20.5617 29.2851C20.5631 29.2818 20.5649 29.2782 20.567 29.2745C20.5691 29.2709 20.5712 29.2678 20.5731 29.2654C20.575 29.2629 20.5763 29.2617 20.5764 29.2615C20.577 29.2609 20.571 29.2671 20.5537 29.2771L21.5491 31.0117ZM22.5606 29.2723H24.5606C24.5606 27.6423 23.6382 26.2843 22.2107 25.6039L21.3502 27.4093L20.4897 29.2148C20.5324 29.2351 20.5547 29.2522 20.5635 29.2597C20.572 29.267 20.5711 29.268 20.5668 29.2614C20.5624 29.2547 20.5596 29.2478 20.5586 29.2441C20.5576 29.2406 20.5606 29.2487 20.5606 29.2723H22.5606ZM21.3502 27.4093L22.2252 25.6109C21.6468 25.3295 20.9345 25.11 20.1044 24.9036L19.6219 26.8445L19.1395 28.7855C19.8928 28.9727 20.2797 29.1127 20.4751 29.2078L21.3502 27.4093ZM19.6219 26.8445L20.1122 24.9056C19.3579 24.7148 18.7803 24.5259 18.3761 24.3554L17.5987 26.1981L16.8213 28.0409C17.4753 28.3168 18.262 28.5636 19.1316 28.7835L19.6219 26.8445ZM17.5987 26.1981L18.4231 24.376C18.3901 24.361 18.3053 24.3178 18.1684 24.1372L16.5742 25.3448L14.98 26.5525C15.4479 27.1702 16.034 27.6854 16.7743 28.0203L17.5987 26.1981ZM16.5742 25.3448L18.1891 24.165C18.2456 24.2423 18.2407 24.2731 18.2227 24.1998C18.2045 24.126 18.1822 23.9831 18.1822 23.7517H16.1822H14.1822C14.1822 24.6452 14.34 25.677 14.9592 26.5246L16.5742 25.3448ZM16.1822 23.7517H18.1822C18.1822 23.3212 18.2884 23.0887 18.4493 22.8948L16.9105 21.6173L15.3716 20.3399C14.5523 21.3268 14.1822 22.4905 14.1822 23.7517H16.1822ZM16.9105 21.6173L18.4394 22.9067C18.5363 22.7917 18.7382 22.621 19.2589 22.4976L18.7977 20.5515L18.3364 18.6054C17.1942 18.8761 16.1571 19.4083 15.3815 20.328L16.9105 21.6173ZM18.7977 20.5515L19.2587 22.4976C20.5396 22.1941 21.5362 21.2773 21.9811 20.1076L20.1118 19.3965L18.2425 18.6855C18.2485 18.6695 18.2594 18.6525 18.2764 18.6374C18.2947 18.6211 18.3169 18.61 18.3366 18.6054L18.7977 20.5515ZM20.1118 19.3965L18.2707 20.1778C18.7748 21.3658 19.8575 22.2642 21.2092 22.4796L21.5241 20.5045L21.8389 18.5295C21.8618 18.5331 21.8889 18.5444 21.9115 18.5625C21.9325 18.5791 21.9456 18.5981 21.9529 18.6153L20.1118 19.3965ZM21.5241 20.5045L21.2095 22.4797C21.6131 22.5439 21.955 22.642 22.239 22.7602L23.0078 20.9139L23.7766 19.0675C23.1678 18.8141 22.5166 18.6374 21.8386 18.5294L21.5241 20.5045ZM23.0078 20.9139L21.1168 20.2625L20.8062 21.1644L22.6971 21.8157L24.5881 22.4671L24.8987 21.5653L23.0078 20.9139ZM22.6971 21.8157L23.4422 19.9597C22.394 19.5389 21.3118 19.3064 20.2222 19.3064V21.3064V23.3064C20.7374 23.3064 21.3144 23.4158 21.952 23.6718L22.6971 21.8157ZM20.2222 21.3064V19.3064C18.3756 19.3064 17.0158 19.9784 16.2041 21.0973C15.479 22.0969 15.4225 23.1457 15.4225 23.5133H17.4225H19.4225C19.4225 23.5364 19.421 23.5167 19.429 23.4838C19.4366 23.4521 19.4446 23.4424 19.4419 23.4461C19.4304 23.4619 19.4309 23.4465 19.496 23.4166C19.572 23.3816 19.7847 23.3064 20.2222 23.3064V21.3064ZM17.4225 23.5133H15.4225C15.4225 25.1215 16.3305 26.4622 17.7248 27.1468L18.6062 25.3515L19.4876 23.5562C19.4466 23.5361 19.4251 23.5193 19.4166 23.5119C19.4083 23.5047 19.4093 23.5038 19.4137 23.5106C19.4182 23.5176 19.4216 23.5255 19.4232 23.5312C19.4248 23.5367 19.4225 23.5321 19.4225 23.5133H17.4225ZM18.6062 25.3515L17.7208 27.1448L17.7418 27.1552L18.6273 25.3619L19.5127 23.5685L19.4916 23.5581L18.6062 25.3515ZM18.6273 25.3619L17.8065 27.1857L17.8279 27.1953L18.6486 25.3715L19.4693 23.5476L19.448 23.538L18.6273 25.3619ZM18.6486 25.3715L17.8335 27.1978C18.3917 27.447 19.0807 27.6446 19.892 27.8337L20.3459 25.8859L20.7998 23.9381C20.0549 23.7645 19.6654 23.6352 19.4637 23.5451L18.6486 25.3715ZM20.3459 25.8859L19.9049 27.8367C20.6333 28.0013 21.173 28.1754 21.5461 28.3389L22.3486 26.5069L23.1511 24.675C22.4698 24.3766 21.6677 24.1342 20.7869 23.9351L20.3459 25.8859ZM22.3486 26.5069L21.5444 28.3381L21.5553 28.3429L22.3595 26.5117L23.1637 24.6806L23.1528 24.6758L22.3486 26.5069ZM22.3595 26.5117L21.5937 28.3593L21.6046 28.3638L22.3705 26.5163L23.1363 24.6687L23.1254 24.6642L22.3595 26.5117ZM22.3705 26.5163L21.6019 28.3627C21.6126 28.3672 21.6778 28.3901 21.8 28.5521L23.3969 27.348L24.9938 26.1439C24.5175 25.5123 23.9113 24.9913 23.139 24.6698L22.3705 26.5163ZM23.3969 27.348L21.822 28.5808L21.8268 28.5869L23.4017 27.3541L24.9766 26.1214L24.9718 26.1153L23.3969 27.348ZM23.4017 27.3541L21.8017 28.5541L21.8065 28.5605L23.4065 27.3605L25.0065 26.1605L25.0017 26.1541L23.4017 27.3541ZM23.4065 27.3605L21.8246 28.5844C21.7504 28.4884 21.748 28.4366 21.7632 28.4965C21.7785 28.5569 21.8009 28.6892 21.8009 28.9149H23.8009H25.8009C25.8009 28.0326 25.6436 26.9837 24.9883 26.1367L23.4065 27.3605ZM23.8009 28.9149H21.8009C21.8009 29.2848 21.7093 29.5141 21.5333 29.7331L23.0923 30.9859L24.6513 32.2387C25.429 31.271 25.8009 30.1456 25.8009 28.9149H23.8009ZM23.0923 30.9859L21.5333 29.733C21.4329 29.8581 21.2474 30.0195 20.7846 30.1498L21.3267 32.0749L21.8688 34.0001C22.9553 33.6941 23.9246 33.143 24.6513 32.2387L23.0923 30.9859ZM21.3267 32.0749L20.7843 30.1499C19.5308 30.5031 18.5813 31.4527 18.177 32.6145L20.0659 33.2717L21.9548 33.929C21.9502 33.9425 21.9426 33.9546 21.9309 33.9659C21.918 33.9783 21.897 33.9921 21.8691 34L21.3267 32.0749Z" fill="#2F6BFF" mask="url(#path-1-inside-1_3567_598)"/>
<path d="M20.2606 18V18.6455C20.2606 19.4201 20.8291 20.0572 21.5663 20.1748C22.2601 20.2853 22.8653 20.4742 23.3925 20.7295L22.87 22.2461C21.9881 21.8536 21.1035 21.6397 20.2225 21.6396C19.6582 21.6396 19.0708 21.7163 18.5936 21.9941C18.1038 22.2795 17.8035 22.7354 17.7362 23.2764L17.7225 23.5137L17.7264 23.6367C17.7467 23.9228 17.839 24.2034 18.0145 24.4541C18.2085 24.7311 18.4674 24.9189 18.7255 25.0459L18.743 25.0547L18.7606 25.0625C19.1501 25.236 19.7178 25.3988 20.4071 25.5596L20.4139 25.5615C21.2518 25.7509 21.9289 25.9645 22.4579 26.1963L22.4755 26.2041C22.8971 26.3796 23.2815 26.6772 23.6268 27.1348L23.6337 27.1436C23.9066 27.4965 24.1005 28.0502 24.1005 28.915C24.1004 29.8088 23.838 30.5589 23.3163 31.208H23.3153C22.9352 31.6813 22.4145 32.0517 21.7118 32.2998L21.3993 32.3984C20.7524 32.581 20.2606 33.1739 20.2606 33.8955V34.667H19.9364V34.0625C19.9363 33.2623 19.3311 32.6121 18.5634 32.5244H18.5624C17.9811 32.4583 17.4213 32.3359 16.8817 32.1572H16.8827C16.5175 32.0342 16.1979 31.8976 15.9188 31.7529L16.4813 30.2197C16.7979 30.3743 17.1266 30.5131 17.4686 30.6328L17.4774 30.6357L17.4852 30.6387C18.2533 30.8924 19.0137 31.0273 19.7606 31.0273C20.322 31.0273 20.916 30.973 21.3739 30.7363L21.3934 30.7266L21.413 30.7158C21.9316 30.4178 22.2606 29.907 22.2606 29.2725C22.2606 28.9402 22.1691 28.6074 21.9628 28.3145C21.7659 28.035 21.5029 27.8465 21.2391 27.7197H21.2401C21.2379 27.7186 21.2355 27.7169 21.2333 27.7158C21.2311 27.7148 21.2286 27.7139 21.2264 27.7129H21.2255C20.9279 27.5686 20.5303 27.4328 20.0594 27.3018L19.5575 27.1699C18.7014 26.9533 18.0144 26.7303 17.4843 26.5059C17.0706 26.3183 16.689 26.0138 16.3436 25.5576C16.0708 25.186 15.8817 24.6173 15.8817 23.752C15.8817 22.8014 16.1586 22.0324 16.6903 21.3916L16.6893 21.3906C17.1528 20.8442 17.8124 20.4444 18.7352 20.2256C19.4129 20.0649 19.9364 19.4576 19.9364 18.7129V18H20.2606Z" fill="black" stroke="#2F6BFF" stroke-width="2"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.3775 1H25.6355C28.519 1 26.9355 5.43733 25.768 8.144L24.4395 11.248L24.26 11.6667C25.5184 11.604 26.763 11.9614 27.8064 12.6853C31.7102 16.3488 34.8784 20.7595 37.1328 25.6693C38.0112 27.4848 38.3598 29.5221 38.1363 31.536C37.868 36.6691 33.8528 40.7595 28.8464 41H11.1665C6.15879 40.7667 2.13764 36.6805 1.86372 31.5467C1.64023 29.5328 1.98873 27.4955 2.86732 25.68C5.12436 20.7653 8.297 16.3507 12.2065 12.6853C13.2502 11.9614 14.4945 11.604 15.7528 11.6667L15.5448 11.184L14.2449 8.144C13.0827 5.43733 11.4915 1 14.3775 1Z" stroke="#2F6BFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M23.2604 10.6666V12.4547C21.0993 12.7469 18.91 12.7326 16.7526 12.4137V10.6666H23.2604ZM30.0261 10.3434C29.1308 10.8033 28.2055 11.197 27.2565 11.5211C27.2225 10.8097 26.938 10.1657 26.4929 9.66858C27.3608 9.3786 28.2074 9.02379 29.027 8.60803L30.0261 10.3434ZM10.9782 8.58362C11.8136 8.99198 12.6735 9.34326 13.5544 9.62952C13.0996 10.1201 12.8051 10.7611 12.7595 11.4713C11.8192 11.1568 10.9001 10.7785 10.0085 10.3375L10.9782 8.58362ZM8.7243 8.52795C8.72643 8.52398 8.72895 8.5202 8.73114 8.51624C8.73411 8.51086 8.73785 8.50591 8.74091 8.50061L8.7243 8.52795Z" fill="black" stroke="#2F6BFF" stroke-width="2"/>
</svg>
<svg width="38" height="38" viewBox="0 0 38 38" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.25 4.19231V1.5" stroke="#2F6BFF" stroke-width="2.5" stroke-linecap="round"/>
<path d="M27.75 4.19231V1.5" stroke="#2F6BFF" stroke-width="2.5" stroke-linecap="round"/>
<path d="M13.75 23.0384L16.375 20.3461V27.5256" stroke="#2F6BFF" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M20.75 25.7307V22.141C20.75 21.1497 21.5335 20.3461 22.5 20.3461C23.4665 20.3461 24.25 21.1497 24.25 22.141V25.7307C24.25 26.7221 23.4665 27.5256 22.5 27.5256C21.5335 27.5256 20.75 26.7221 20.75 25.7307Z" stroke="#2F6BFF" stroke-width="2.5" stroke-linecap="round"/>
<path d="M35.625 13.1667H27.0938H16.8125M1.5 13.1667H8.28125" stroke="#2F6BFF" stroke-width="2.5" stroke-linecap="round"/>
<path d="M22.5 36.5H15.5C8.90033 36.5 5.60051 36.5 3.55025 34.3971C1.5 32.2944 1.5 28.9098 1.5 22.141V18.5513C1.5 11.7824 1.5 8.39794 3.55025 6.29511C5.60051 4.19229 8.90033 4.19229 15.5 4.19229H22.5C29.0996 4.19229 32.3996 4.19229 34.4497 6.29511C36.5 8.39794 36.5 11.7824 36.5 18.5513V22.141C36.5 28.9098 36.5 32.2944 34.4497 34.3971C33.3066 35.5695 31.7752 36.0882 29.5 36.3178" stroke="#2F6BFF" stroke-width="2.5" stroke-linecap="round"/>
</svg>
<svg width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2 5.5H18V2.5H2V5.5ZM12 16.5V7.5H8V16.5H12ZM14 16.5H18V7.5H14V16.5ZM6 16.5V7.5H2V16.5H6ZM1 0.5H19C19.2652 0.5 19.5196 0.605357 19.7071 0.792893C19.8946 0.98043 20 1.23478 20 1.5V17.5C20 17.7652 19.8946 18.0196 19.7071 18.2071C19.5196 18.3946 19.2652 18.5 19 18.5H1C0.734784 18.5 0.48043 18.3946 0.292893 18.2071C0.105357 18.0196 0 17.7652 0 17.5V1.5C0 1.23478 0.105357 0.98043 0.292893 0.792893C0.48043 0.605357 0.734784 0.5 1 0.5Z" fill="black"/>
</svg>
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 20H20V0H0V20ZM2 18V13H6V18H2ZM8 18V13H12V18H8ZM14 18V13H18V18H14ZM18 11H14V6H18V11ZM18 4H2V2H18V4ZM2 6H6V11H2V6ZM8 11V6H12V11H8Z" fill="black"/>
</svg>
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M19 20H1C0.734784 20 0.48043 19.8946 0.292893 19.7071C0.105357 19.5196 0 19.2652 0 19V1C0 0.734784 0.105357 0.48043 0.292893 0.292893C0.48043 0.105357 0.734784 0 1 0H19C19.2652 0 19.5196 0.105357 19.7071 0.292893C19.8946 0.48043 20 0.734784 20 1V19C20 19.2652 19.8946 19.5196 19.7071 19.7071C19.5196 19.8946 19.2652 20 19 20ZM2 18H18V2H2V18Z" fill="black"/>
<path d="M7 20C6.73478 20 6.48043 19.8946 6.29289 19.7071C6.10536 19.5196 6 19.2652 6 19V1C6 0.734784 6.10536 0.48043 6.29289 0.292893C6.48043 0.105357 6.73478 0 7 0C7.26522 0 7.51957 0.105357 7.70711 0.292893C7.89464 0.48043 8 0.734784 8 1V19C8 19.2652 7.89464 19.5196 7.70711 19.7071C7.51957 19.8946 7.26522 20 7 20ZM13 20C12.7348 20 12.4804 19.8946 12.2929 19.7071C12.1054 19.5196 12 19.2652 12 19V1C12 0.734784 12.1054 0.48043 12.2929 0.292893C12.4804 0.105357 12.7348 0 13 0C13.2652 0 13.5196 0.105357 13.7071 0.292893C13.8946 0.48043 14 0.734784 14 1V19C14 19.2652 13.8946 19.5196 13.7071 19.7071C13.5196 19.8946 13.2652 20 13 20Z" fill="black"/>
<path d="M19 8H1C0.734784 8 0.48043 7.89464 0.292893 7.70711C0.105357 7.51957 0 7.26522 0 7C0 6.73478 0.105357 6.48043 0.292893 6.29289C0.48043 6.10536 0.734784 6 1 6H19C19.2652 6 19.5196 6.10536 19.7071 6.29289C19.8946 6.48043 20 6.73478 20 7C20 7.26522 19.8946 7.51957 19.7071 7.70711C19.5196 7.89464 19.2652 8 19 8ZM19 14H1C0.734784 14 0.48043 13.8946 0.292893 13.7071C0.105357 13.5196 0 13.2652 0 13C0 12.7348 0.105357 12.4804 0.292893 12.2929C0.48043 12.1054 0.734784 12 1 12H19C19.2652 12 19.5196 12.1054 19.7071 12.2929C19.8946 12.4804 20 12.7348 20 13C20 13.2652 19.8946 13.5196 19.7071 13.7071C19.5196 13.8946 19.2652 14 19 14Z" fill="black"/>
</svg>
<svg width="10" height="10" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1.66333 7.14474H11.6633M6.66333 1.8158V12.4737" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.5 6.25C4.81 6.25 4.25 5.69 4.25 5C4.25 4.31 4.81 3.75 5.5 3.75C6.19 3.75 6.75 4.31 6.75 5C6.75 5.69 6.19 6.25 5.5 6.25ZM5.5 2.5C4.11937 2.5 3 3.61937 3 5C3 6.38063 4.11937 7.5 5.5 7.5C6.88063 7.5 8 6.38063 8 5C8 3.61937 6.88063 2.5 5.5 2.5ZM19.25 10.705L15.5 6.875L9.28687 13.1944L6.75 10.625L1.75 15.2106V2.5C1.75 1.81 2.31 1.25 3 1.25H18C18.69 1.25 19.25 1.81 19.25 2.5V10.705ZM19.25 17.5C19.25 18.19 18.69 18.75 18 18.75H14.77L10.165 14.0844L15.5 8.74938L19.25 12.4994V17.5ZM3 18.75C2.31 18.75 1.75 18.19 1.75 17.5V16.9131L6.71563 12.4656L13.0006 18.75H3ZM18 0H3C1.61937 0 0.5 1.11937 0.5 2.5V17.5C0.5 18.8806 1.61937 20 3 20H18C19.3806 20 20.5 18.8806 20.5 17.5V2.5C20.5 1.11937 19.3806 0 18 0Z" fill="#2F6BFF"/>
</svg>
<svg width="38" height="37" viewBox="0 0 38 37" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M28.7222 16.5556H9.27777M24.8333 16.5556V20.4444M26.7778 8.77778H11.2222V2.94444C11.2222 2.42875 11.4271 1.93417 11.7917 1.56951C12.1564 1.20486 12.651 1 13.1667 1H24.8333C25.349 1 25.8436 1.20486 26.2083 1.56951C26.5729 1.93417 26.7778 2.42875 26.7778 2.94444V8.77778Z" stroke="#2F6BFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M36.5 34.0555V10.7222C36.5 9.64833 35.6294 8.77777 34.5556 8.77777L3.44445 8.77777C2.37056 8.77777 1.5 9.64833 1.5 10.7222V34.0555C1.5 35.1294 2.37056 36 3.44445 36H34.5556C35.6294 36 36.5 35.1294 36.5 34.0555Z" stroke="#2F6BFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<svg width="36" height="35" viewBox="0 0 36 35" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17.9999 17.093C18.6742 17.093 19.2209 17.6397 19.2209 18.3139V19.5349H20.4418C21.1161 19.5349 21.6627 20.0815 21.6627 20.7558C21.6627 21.4301 21.1161 21.9767 20.4418 21.9767H19.2209V23.1977C19.2209 23.8719 18.6742 24.4186 17.9999 24.4186C17.3257 24.4186 16.779 23.8719 16.779 23.1977V21.9767H15.5581C14.8838 21.9767 14.3372 21.4301 14.3372 20.7558C14.3372 20.0815 14.8838 19.5349 15.5581 19.5349H16.779V18.3139C16.779 17.6397 17.3257 17.093 17.9999 17.093Z" fill="#2F6BFF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M18 13.0233C13.7294 13.0233 10.2675 16.4853 10.2675 20.7558C10.2675 25.0265 13.7294 28.4884 18 28.4884C22.2707 28.4884 25.7326 25.0265 25.7326 20.7558C25.7326 16.4853 22.2707 13.0233 18 13.0233ZM12.7093 20.7558C12.7093 17.8339 15.0781 15.4651 18 15.4651C20.9219 15.4651 23.2907 17.8339 23.2907 20.7558C23.2907 23.6777 20.9219 26.0465 18 26.0465C15.0781 26.0465 12.7093 23.6777 12.7093 20.7558Z" fill="#2F6BFF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.9153 1.87354e-06C16.4527 -4.69637e-05 15.233 -7.94682e-05 14.2648 0.130088C13.2428 0.2675 12.3201 0.569753 11.5787 1.31122C10.8372 2.05268 10.5349 2.97538 10.3975 3.99736C10.3013 4.71299 10.2762 5.56596 10.2697 6.55286C9.21399 6.58674 8.27285 6.64847 7.43648 6.76093C5.52789 7.01753 3.98309 7.55818 2.76481 8.77646C1.54655 9.99472 1.00589 11.5395 0.749298 13.4481C0.499952 15.3027 0.499968 17.6722 0.500001 20.664V20.8476C0.499968 23.8394 0.499952 26.209 0.749298 28.0635C1.00589 29.9721 1.54655 31.5169 2.76481 32.7353C3.98309 33.9534 5.52789 34.4941 7.43648 34.7508C9.29099 35 11.6606 35 14.6523 35H21.3476C24.3394 35 26.709 35 28.5635 34.7508C30.472 34.4941 32.0169 33.9534 33.2353 32.7353C34.4534 31.5169 34.994 29.9721 35.2508 28.0635C35.5 26.209 35.5 23.8394 35.5 20.8478V20.664C35.5 17.6724 35.5 15.3027 35.2508 13.4481C34.994 11.5395 34.4534 9.99472 33.2353 8.77646C32.0169 7.55818 30.472 7.01753 28.5635 6.76093C27.7271 6.64847 26.786 6.58674 25.7303 6.55286C25.7238 5.56596 25.6987 4.71299 25.6025 3.99736C25.4651 2.97538 25.1628 2.05268 24.4213 1.31122C23.6799 0.569753 22.7572 0.2675 21.7352 0.130088C20.767 -7.94682e-05 19.5473 -4.69637e-05 18.0847 1.87354e-06H17.9153ZM23.2878 6.51453C23.2809 5.5861 23.2581 4.88576 23.1824 4.32275C23.0813 3.57152 22.9068 3.25002 22.6947 3.03787C22.4826 2.82572 22.1611 2.65118 21.4098 2.55018C20.6235 2.44447 19.5693 2.44186 18 2.44186C16.4307 2.44186 15.3765 2.44447 14.5902 2.55018C13.839 2.65118 13.5174 2.82572 13.3053 3.03787C13.0932 3.25002 12.9186 3.57152 12.8176 4.32275C12.7419 4.88576 12.7191 5.5861 12.7122 6.51453C13.3278 6.51163 13.9741 6.51163 14.6523 6.51163H21.3476C22.026 6.51163 22.6723 6.51163 23.2878 6.51453ZM4.49147 10.5031C5.18041 9.81417 6.12403 9.40121 7.76185 9.18101C9.43478 8.95609 11.6401 8.95349 14.7442 8.95349H21.2558C24.3599 8.95349 26.5652 8.95609 28.2382 9.18101C29.8759 9.40121 30.8196 9.81417 31.5085 10.5031C32.1975 11.1921 32.6105 12.1357 32.8306 13.7735C33.0555 15.4464 33.0581 17.6517 33.0581 20.7558C33.0581 23.8599 33.0555 26.0652 32.8306 27.7382C32.6105 29.3759 32.1975 30.3196 31.5085 31.0085C30.8196 31.6975 29.8759 32.1105 28.2382 32.3306C26.5652 32.5555 24.3599 32.5581 21.2558 32.5581H14.7442C11.6401 32.5581 9.43478 32.5555 7.76185 32.3306C6.12403 32.1105 5.18041 31.6975 4.49147 31.0085C3.80252 30.3196 3.38958 29.3759 3.16938 27.7382C2.94445 26.0652 2.94186 23.8599 2.94186 20.7558C2.94186 17.6517 2.94445 15.4464 3.16938 13.7735C3.38958 12.1357 3.80252 11.1921 4.49147 10.5031Z" fill="#2F6BFF"/>
</svg>
<svg width="23" height="16" viewBox="0 0 23 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 8.00098H22" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M1 14.2998H22" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M1 1.7002H22" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<svg width="21" height="23" viewBox="0 0 21 23" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17.2491 9.20957V8.50497C17.2491 4.63623 14.2274 1.5 10.5 1.5C6.77256 1.5 3.75087 4.63623 3.75087 8.50497V9.20957C3.75087 10.0552 3.50972 10.8818 3.0578 11.5854L1.95036 13.3095C0.938821 14.8843 1.71105 17.0249 3.47036 17.5229C8.07274 18.8257 12.9273 18.8257 17.5296 17.5229C19.289 17.0249 20.0612 14.8843 19.0496 13.3095L17.9422 11.5854C17.4903 10.8818 17.2491 10.0552 17.2491 9.20957Z" stroke="#6C6C6C" stroke-width="1.5"/>
<path d="M6 18.5C6.65503 20.2478 8.42246 21.5 10.5 21.5C12.5775 21.5 14.345 20.2478 15 18.5" stroke="#6C6C6C" stroke-width="1.5" stroke-linecap="round"/>
</svg>
<svg width="46" height="35" viewBox="0 0 46 35" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.7184 4.75186C13.6079 4.75186 15.1452 6.28916 15.1452 8.17873C15.1452 10.0683 13.6079 11.6056 11.7184 11.6056C10.3638 11.6056 9.13381 10.8043 8.58483 9.56409L7.73706 9.93935C8.43446 11.5147 9.99716 12.5326 11.7184 12.5326C14.1191 12.5326 16.0722 10.5795 16.0722 8.17873C16.0722 5.77799 14.1191 3.82483 11.7184 3.82483C9.31857 3.82483 7.36615 5.77799 7.36615 8.17873H8.29318C8.29318 6.28916 9.82974 4.75186 11.7184 4.75186Z" fill="#2F6BFF"/>
<path d="M16.0323 28.0702L16.6114 28.7943C16.9178 28.5491 17.2085 28.2787 17.4755 27.9905L16.7953 27.3606C16.5596 27.6151 16.3029 27.8539 16.0323 28.0702Z" fill="#2F6BFF"/>
<path d="M3.80524 22.6106L4.73227 22.6124C4.73292 22.2648 4.75971 21.9151 4.81199 21.573L3.89553 21.4332C3.83639 21.8208 3.80607 22.217 3.80524 22.6106Z" fill="#2F6BFF"/>
<path d="M19.5665 22.249C19.5533 21.9777 19.5259 21.705 19.4854 21.4382L18.5688 21.5776C18.6047 21.8132 18.6287 22.0545 18.6405 22.2942C18.6457 22.4013 18.6486 22.51 18.6489 22.6174L19.5759 22.6145C19.5756 22.493 19.5724 22.3702 19.5665 22.249Z" fill="#2F6BFF"/>
<path d="M14.5739 29.9734C14.9393 29.8298 15.2972 29.6572 15.6376 29.4602L15.173 28.6579C14.873 28.8317 14.5573 28.984 14.2352 29.1105L14.5739 29.9734Z" fill="#2F6BFF"/>
<path d="M11.0982 30.4955L11.1667 29.571C10.8207 29.5453 10.4743 29.4933 10.1369 29.4162L9.93048 30.3201C10.3132 30.4073 10.7059 30.4663 11.0982 30.4955Z" fill="#2F6BFF"/>
<path d="M18.801 26.0448L17.9655 25.643C17.8154 25.9551 17.6402 26.2589 17.4448 26.5457L18.211 27.0676C18.4324 26.7427 18.6309 26.3984 18.801 26.0448Z" fill="#2F6BFF"/>
<path d="M19.4915 23.7913L18.5744 23.6561C18.5238 23.9992 18.4466 24.3412 18.345 24.6724L19.2313 24.9442C19.3465 24.5685 19.434 24.1806 19.4915 23.7913Z" fill="#2F6BFF"/>
<path d="M12.278 30.4957C12.6708 30.4666 13.0635 30.4078 13.445 30.3212L13.2397 29.4171C12.9031 29.4936 12.5562 29.5455 12.2092 29.5712L12.278 30.4957Z" fill="#2F6BFF"/>
<path d="M5.03506 24.6685C4.9342 24.3379 4.85744 23.996 4.80692 23.6523L3.88971 23.7871C3.94691 24.1765 4.03396 24.5641 4.14835 24.939L5.03506 24.6685Z" fill="#2F6BFF"/>
<path d="M6.60984 17.878L5.93283 17.2447C5.66445 17.5316 5.41545 17.8409 5.19287 18.1642L5.95628 18.69C6.153 18.4046 6.3728 18.1314 6.60984 17.878Z" fill="#2F6BFF"/>
<path d="M7.73865 29.4566C8.07887 29.6539 8.43661 29.827 8.80177 29.9711L9.1418 29.1087C8.81966 28.9816 8.5041 28.8289 8.20383 28.6548L7.73865 29.4566Z" fill="#2F6BFF"/>
<path d="M5.9325 26.5406C5.73754 26.254 5.5628 25.9505 5.41327 25.6385L4.57727 26.0394C4.74692 26.3931 4.94493 26.7372 5.16603 27.0621L5.9325 26.5406Z" fill="#2F6BFF"/>
<path d="M4.16162 20.2817L5.04647 20.558C5.14974 20.2276 5.27907 19.9018 5.43101 19.5898L4.5976 19.1839C4.42536 19.5376 4.27871 19.9069 4.16162 20.2817Z" fill="#2F6BFF"/>
<path d="M16.7759 17.8826C17.013 18.1361 17.2326 18.4094 17.4288 18.6946L18.1927 18.1693C17.9704 17.8461 17.7215 17.5366 17.453 17.2495L16.7759 17.8826Z" fill="#2F6BFF"/>
<path d="M6.76472 28.7894L7.34402 28.0656C7.07324 27.8489 6.81663 27.6101 6.58126 27.3557L5.90082 27.9854C6.16743 28.2735 6.45806 28.544 6.76472 28.7894Z" fill="#2F6BFF"/>
<path d="M19.2212 20.2872C19.1047 19.9121 18.9586 19.5427 18.7869 19.1893L17.953 19.5943C18.1044 19.9058 18.2332 20.2315 18.3359 20.5621L19.2212 20.2872Z" fill="#2F6BFF"/>
<path d="M35.2368 34.06H1.42703V3.60052H4.90878C3.99204 4.93119 3.45445 6.54227 3.45445 8.27684C3.45445 10.4164 5.22379 13.5337 7.09074 16.2274C6.99275 16.2979 6.8955 16.3708 6.80141 16.4452L7.37663 17.1722C7.45756 17.1081 7.54164 17.046 7.62591 16.9852C8.24693 17.8486 8.861 18.6505 9.4072 19.3397C8.30969 20.0963 7.6401 21.3507 7.6401 22.6977C7.6401 24.9468 9.46987 26.7766 11.719 26.7766C13.9682 26.7766 15.798 24.9468 15.798 22.6977C15.798 21.3502 15.1283 20.0957 14.0305 19.3391C14.5711 18.6571 15.1782 17.8649 15.7926 17.0117C15.866 17.0653 15.9383 17.1201 16.0093 17.1763L16.5849 16.4497C16.5009 16.383 16.4149 16.3183 16.3279 16.255C18.2021 13.555 19.9839 10.4237 19.9839 8.27684C19.9839 6.54237 19.4462 4.93128 18.5294 3.60052H24.2671V2.67349H17.7879C16.2767 1.03858 14.1153 0.0130005 11.7185 0.0130005C9.3221 0.0130005 7.1611 1.03858 5.65031 2.67349H0.5V34.9871H35.2368V34.06ZM14.8709 22.6975C14.8709 24.4355 13.4569 25.8494 11.7189 25.8494C9.98094 25.8494 8.56703 24.4355 8.56703 22.6975C8.56703 21.6337 9.10657 20.6435 9.98845 20.0633C10.1015 20.2021 10.2091 20.3331 10.3115 20.457C9.58297 20.9296 9.129 21.7466 9.129 22.6313C9.129 24.0593 10.2908 25.2211 11.7189 25.2211C13.1472 25.2211 14.3094 24.0594 14.3094 22.6313C14.3094 21.7462 13.8549 20.9289 13.1255 20.4565C13.2281 20.3325 13.3357 20.2014 13.4488 20.0627C14.3311 20.6427 14.8709 21.6332 14.8709 22.6975ZM12.5226 21.1752C13.0488 21.466 13.3824 22.0237 13.3824 22.6314C13.3824 23.5483 12.6362 24.2942 11.7189 24.2942C10.8021 24.2942 10.0561 23.5483 10.0561 22.6314C10.0561 22.0242 10.3892 21.4668 10.9146 21.1758C11.1662 21.4711 11.3296 21.6565 11.3712 21.7035L11.7182 22.0956L12.0653 21.7035C12.1071 21.6564 12.2706 21.4707 12.5226 21.1752ZM11.7183 0.93994C15.7647 0.93994 19.0568 4.23118 19.0568 8.27675C19.0568 11.8536 13.2177 18.9295 11.7183 20.6872C10.2187 18.9292 4.38148 11.8549 4.38148 8.27675C4.38148 4.23128 7.67282 0.93994 11.7183 0.93994Z" fill="#2F6BFF"/>
<path d="M28.7167 3.60068H44.573V34.0603H36.9463V34.9873H45.5V2.67365H28.7167V3.60068Z" fill="#2F6BFF"/>
<path d="M27.2951 2.67365H25.7028V3.60068H27.2951V2.67365Z" fill="#2F6BFF"/>
<path d="M21.5406 28.9374L25.8386 24.2273H42.2788V9.45575H21.5406V28.9374ZM22.4677 10.3828H41.3518V23.3003H25.4294L22.4677 26.5462V10.3828Z" fill="#2F6BFF"/>
<path d="M38.6314 13.8314H27.357V14.7585H38.6314V13.8314Z" fill="#2F6BFF"/>
<path d="M26.3409 13.8314H25.1886V14.7585H26.3409V13.8314Z" fill="#2F6BFF"/>
<path d="M34.5985 19.1265H25.1886V20.0535H34.5985V19.1265Z" fill="#2F6BFF"/>
<path d="M38.6313 16.4789H36.767V17.4059H38.6313V16.4789Z" fill="#2F6BFF"/>
<path d="M35.8067 16.4789H25.1886V17.4059H35.8067V16.4789Z" fill="#2F6BFF"/>
</svg>
<svg width="20" height="23" viewBox="0 0 20 23" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10 9.92857C12.7614 9.92857 15 7.81791 15 5.21429C15 2.61066 12.7614 0.5 10 0.5C7.23858 0.5 5 2.61066 5 5.21429C5 7.81791 7.23858 9.92857 10 9.92857Z" fill="#4B4B4B" fill-opacity="0.8"/>
<path d="M20 17.7857C20 15.182 17.7615 13.0714 15 13.0714H5C2.23858 13.0714 0 15.182 0 17.7857V22.5H20V17.7857Z" fill="#4B4B4B" fill-opacity="0.8"/>
</svg>
<svg width="36" height="35" viewBox="0 0 36 35" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M25.2917 7C25.1306 7 25 7.13058 25 7.29167V10.2083C25 10.3694 25.1306 10.5 25.2917 10.5H28.2083C28.3694 10.5 28.5 10.3694 28.5 10.2083V7.29167C28.5 7.13058 28.3694 7 28.2083 7H25.2917ZM25 28V27.125C25 26.6418 25.3918 26.25 25.875 26.25C26.3582 26.25 26.75 26.6418 26.75 27.125V28H28.5V27.125C28.5 26.6418 28.8918 26.25 29.375 26.25C29.8582 26.25 30.25 26.6418 30.25 27.125V28.875C30.25 29.3582 29.8582 29.75 29.375 29.75H22.375C21.8918 29.75 21.5 29.3582 21.5 28.875V27.125C21.5 26.6418 21.8918 26.25 22.375 26.25C22.8582 26.25 23.25 26.6418 23.25 27.125V28H25ZM19.75 15.75H20.625C21.1082 15.75 21.5 16.1418 21.5 16.625C21.5 17.1082 21.1082 17.5 20.625 17.5H17.125C16.6418 17.5 16.25 17.1082 16.25 16.625C16.25 16.1418 16.6418 15.75 17.125 15.75H18V14H15.375C14.8918 14 14.5 13.6082 14.5 13.125C14.5 12.6418 14.8918 12.25 15.375 12.25H20.625C21.1082 12.25 21.5 12.6418 21.5 13.125C21.5 13.6082 21.1082 14 20.625 14H19.75V15.75ZM28.5 17.5H27.625C27.1418 17.5 26.75 17.1082 26.75 16.625C26.75 16.1418 27.1418 15.75 27.625 15.75H28.5V14.875C28.5 14.3918 28.8918 14 29.375 14C29.8582 14 30.25 14.3918 30.25 14.875V18.375C30.25 18.8582 29.8582 19.25 29.375 19.25C28.8918 19.25 28.5 18.8582 28.5 18.375V17.5ZM19.75 21H18.875C18.3918 21 18 20.6082 18 20.125C18 19.6418 18.3918 19.25 18.875 19.25H20.625C21.1082 19.25 21.5 19.6418 21.5 20.125V23.625C21.5 24.1082 21.1082 24.5 20.625 24.5H15.375C14.8918 24.5 14.5 24.1082 14.5 23.625C14.5 23.1418 14.8918 22.75 15.375 22.75H19.75V21ZM25.2917 5.25H28.2083C29.3359 5.25 30.25 6.16408 30.25 7.29167V10.2083C30.25 11.3359 29.3359 12.25 28.2083 12.25H25.2917C24.1641 12.25 23.25 11.3359 23.25 10.2083V7.29167C23.25 6.16408 24.1641 5.25 25.2917 5.25ZM7.79167 5.25H10.7083C11.8359 5.25 12.75 6.16408 12.75 7.29167V10.2083C12.75 11.3359 11.8359 12.25 10.7083 12.25H7.79167C6.66408 12.25 5.75 11.3359 5.75 10.2083V7.29167C5.75 6.16408 6.66408 5.25 7.79167 5.25ZM7.79167 7C7.63058 7 7.5 7.13058 7.5 7.29167V10.2083C7.5 10.3694 7.63058 10.5 7.79167 10.5H10.7083C10.8694 10.5 11 10.3694 11 10.2083V7.29167C11 7.13058 10.8694 7 10.7083 7H7.79167ZM7.79167 22.75H10.7083C11.8359 22.75 12.75 23.6641 12.75 24.7917V27.7083C12.75 28.8359 11.8359 29.75 10.7083 29.75H7.79167C6.66408 29.75 5.75 28.8359 5.75 27.7083V24.7917C5.75 23.6641 6.66408 22.75 7.79167 22.75ZM7.79167 24.5C7.63058 24.5 7.5 24.6306 7.5 24.7917V27.7083C7.5 27.8694 7.63058 28 7.79167 28H10.7083C10.8694 28 11 27.8694 11 27.7083V24.7917C11 24.6306 10.8694 24.5 10.7083 24.5H7.79167ZM19.75 7H15.375C14.8918 7 14.5 6.60825 14.5 6.125C14.5 5.64175 14.8918 5.25 15.375 5.25H20.625C21.1082 5.25 21.5 5.64175 21.5 6.125V9.625C21.5 10.1082 21.1082 10.5 20.625 10.5C20.1418 10.5 19.75 10.1082 19.75 9.625V7ZM15.375 10.5C14.8918 10.5 14.5 10.1082 14.5 9.625C14.5 9.14175 14.8918 8.75 15.375 8.75H17.125C17.6082 8.75 18 9.14175 18 9.625C18 10.1082 17.6082 10.5 17.125 10.5H15.375ZM6.625 21C6.14175 21 5.75 20.6082 5.75 20.125C5.75 19.6418 6.14175 19.25 6.625 19.25H10.125C10.6082 19.25 11 19.6418 11 20.125C11 20.6082 10.6082 21 10.125 21H6.625ZM13.625 21C13.1418 21 12.75 20.6082 12.75 20.125C12.75 19.6418 13.1418 19.25 13.625 19.25H15.375C15.8582 19.25 16.25 19.6418 16.25 20.125C16.25 20.6082 15.8582 21 15.375 21H13.625ZM16.25 28V28.875C16.25 29.3582 15.8582 29.75 15.375 29.75C14.8918 29.75 14.5 29.3582 14.5 28.875V27.125C14.5 26.6418 14.8918 26.25 15.375 26.25H18.875C19.3582 26.25 19.75 26.6418 19.75 27.125C19.75 27.6082 19.3582 28 18.875 28H16.25ZM12.75 15.75H13.625C14.1082 15.75 14.5 16.1418 14.5 16.625C14.5 17.1082 14.1082 17.5 13.625 17.5H11.875C11.3918 17.5 11 17.1082 11 16.625V15.75H10.125C9.64175 15.75 9.25 15.3582 9.25 14.875C9.25 14.3918 9.64175 14 10.125 14H11.875C12.3582 14 12.75 14.3918 12.75 14.875V15.75ZM5.75 14.875C5.75 14.3918 6.14175 14 6.625 14C7.10825 14 7.5 14.3918 7.5 14.875V16.625C7.5 17.1082 7.10825 17.5 6.625 17.5C6.14175 17.5 5.75 17.1082 5.75 16.625V14.875ZM23.25 14.875C23.25 14.3918 23.6418 14 24.125 14C24.6082 14 25 14.3918 25 14.875V18.375C25 18.8582 24.6082 19.25 24.125 19.25C23.6418 19.25 23.25 18.8582 23.25 18.375V14.875ZM26.75 22.75V21.875C26.75 21.3918 27.1418 21 27.625 21H29.375C29.8582 21 30.25 21.3918 30.25 21.875C30.25 22.3582 29.8582 22.75 29.375 22.75H28.5V23.625C28.5 24.1082 28.1082 24.5 27.625 24.5H24.125C23.6418 24.5 23.25 24.1082 23.25 23.625V21.875C23.25 21.3918 23.6418 21 24.125 21C24.6082 21 25 21.3918 25 21.875V22.75H26.75ZM2.25 7.875C2.25 8.35825 1.85825 8.75 1.375 8.75C0.891751 8.75 0.5 8.35825 0.5 7.875V4.375C0.5 1.95875 2.45875 0 4.875 0H8.375C8.85825 0 9.25 0.391751 9.25 0.875C9.25 1.35825 8.85825 1.75 8.375 1.75H4.875C3.42525 1.75 2.25 2.92525 2.25 4.375V7.875ZM27.625 1.75C27.1418 1.75 26.75 1.35825 26.75 0.875C26.75 0.391751 27.1418 0 27.625 0H31.125C33.5412 0 35.5 1.95875 35.5 4.375V7.875C35.5 8.35825 35.1082 8.75 34.625 8.75C34.1418 8.75 33.75 8.35825 33.75 7.875V4.375C33.75 2.92525 32.5747 1.75 31.125 1.75H27.625ZM8.375 33.25C8.85825 33.25 9.25 33.6418 9.25 34.125C9.25 34.6082 8.85825 35 8.375 35H4.875C2.45875 35 0.5 33.0412 0.5 30.625V27.125C0.5 26.6418 0.891751 26.25 1.375 26.25C1.85825 26.25 2.25 26.6418 2.25 27.125V30.625C2.25 32.0747 3.42525 33.25 4.875 33.25H8.375ZM33.75 27.125C33.75 26.6418 34.1418 26.25 34.625 26.25C35.1082 26.25 35.5 26.6418 35.5 27.125V30.625C35.5 33.0412 33.5412 35 31.125 35H27.625C27.1418 35 26.75 34.6082 26.75 34.125C26.75 33.6418 27.1418 33.25 27.625 33.25H31.125C32.5747 33.25 33.75 32.0747 33.75 30.625V27.125Z" fill="#6C6C6C"/>
</svg>
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.0146484 0H3.64665V3.62266H0.0146484V0ZM9.07389 9.0872H10V10H9.07389V9.0872ZM7.29411 9.08558H8.22021V9.95518H7.29411H7.29329H6.368V8.17685H7.26481V7.27547H8.17708V5.4784H9.10319V6.36186H9.98535V7.27465H9.10319V8.18745H8.19092H8.17708H7.29411V9.08558ZM4.54427 7.2608H5.44108V6.348H4.57357V5.43521H5.44108V4.52241H4.55892V5.43521H3.632V4.52241H4.54346V1.81174H5.46956V4.5216H6.36637V5.43439H7.24854V4.5216H8.17464V5.43439H7.29411V6.34719H6.368V8.14425H5.47038V9.95599H4.54427V7.2608ZM9.05843 4.5216H9.98454V5.43439H9.05843V4.5216ZM1.82373 4.5216H2.74984V5.43439H1.82373V4.5216ZM0.0146484 4.5216H0.940755V5.43439H0.0146484V4.5216ZM4.54427 0H5.47038V0.912795H4.54427V0ZM0 6.36186H3.632V9.98452H0V6.36186ZM0.882161 7.24205H2.74984V9.10513H0.882161V7.24205ZM6.35254 0H9.98454V3.62266H6.35254V0ZM7.23551 0.880196H9.10319V2.74328H7.23551V0.880196ZM0.89681 0.880196H2.76449V2.74328H0.89681V0.880196Z" fill="white"/>
</svg>
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M38.3333 1.66644V31.6664H33.3333V29.9998H36.6666V3.33311H3.33329V29.9998H16.6666V31.6664H1.66663V1.66644H38.3333ZM26.6666 4.99978H13.3333V6.66644H26.6666V4.99978ZM23.3333 9.99978V8.33311H16.6666V9.99978H23.3333ZM11.6666 18.3331H4.99996V19.9998H11.6666V18.3331ZM11.6666 21.6664H4.99996V23.3331H11.6666V21.6664ZM4.99996 26.6664H8.33329V24.9998H4.99996V26.6664ZM28.3333 21.6664C28.3333 22.3257 28.1378 22.9702 27.7715 23.5183C27.4053 24.0665 26.8847 24.4937 26.2756 24.746C25.6665 24.9983 24.9963 25.0643 24.3497 24.9357C23.7031 24.8071 23.1091 24.4896 22.6429 24.0235C22.1768 23.5573 21.8593 22.9633 21.7307 22.3167C21.6021 21.6701 21.6681 20.9999 21.9204 20.3908C22.1727 19.7817 22.5999 19.2611 23.1481 18.8949C23.6962 18.5286 24.3407 18.3331 25 18.3331C25.8837 18.334 26.7311 18.6855 27.356 19.3104C27.9809 19.9353 28.3324 20.7827 28.3333 21.6664ZM26.6666 21.6664C26.6666 21.3368 26.5689 21.0146 26.3857 20.7405C26.2026 20.4664 25.9423 20.2528 25.6378 20.1266C25.3332 20.0005 24.9981 19.9675 24.6748 20.0318C24.3515 20.0961 24.0545 20.2548 23.8215 20.4879C23.5884 20.721 23.4296 21.018 23.3653 21.3413C23.301 21.6646 23.334 21.9997 23.4602 22.3042C23.5863 22.6088 23.7999 22.8691 24.074 23.0522C24.3481 23.2354 24.6703 23.3331 25 23.3331C25.4419 23.3327 25.8655 23.1569 26.178 22.8445C26.4905 22.532 26.6662 22.1083 26.6666 21.6664ZM26.67 14.5114C26.7452 14.723 26.8638 14.9164 27.0183 15.0793C27.1727 15.2422 27.3596 15.371 27.5668 15.4574C27.774 15.5437 27.997 15.5857 28.2215 15.5807C28.4459 15.5756 28.6668 15.5237 28.87 15.4281C29.202 15.273 29.5735 15.2229 29.9347 15.2845C30.296 15.3462 30.6298 15.5166 30.8916 15.7731C31.1508 16.0321 31.3236 16.365 31.3862 16.726C31.4489 17.087 31.3984 17.4586 31.2416 17.7898C31.1451 17.9931 31.0923 18.2143 31.0866 18.4393C31.0809 18.6643 31.1224 18.8879 31.2085 19.0958C31.2946 19.3038 31.4233 19.4913 31.5863 19.6465C31.7493 19.8016 31.943 19.9208 32.155 19.9964C32.4994 20.1195 32.7974 20.346 33.008 20.6449C33.2187 20.9439 33.3318 21.3007 33.3318 21.6664C33.3318 22.0322 33.2187 22.389 33.008 22.6879C32.7974 22.9869 32.4994 23.2134 32.155 23.3364C31.9429 23.4117 31.7491 23.5306 31.5859 23.6855C31.4227 23.8404 31.2938 24.0278 31.2076 24.2356C31.1213 24.4435 31.0797 24.6671 31.0852 24.892C31.0908 25.1169 31.1435 25.3382 31.24 25.5414C31.3576 25.7828 31.4182 26.0479 31.417 26.3164C31.4157 26.5849 31.3527 26.8495 31.2329 27.0898C31.113 27.33 30.9394 27.5395 30.7256 27.7019C30.5119 27.8643 30.2636 27.9754 30 28.0264V39.7698L25 35.5398L20 39.7698V28.0264C19.7366 27.9753 19.4885 27.8643 19.2749 27.7019C19.0613 27.5396 18.8879 27.3304 18.768 27.0903C18.6482 26.8503 18.5851 26.5859 18.5837 26.3177C18.5823 26.0494 18.6426 25.7844 18.76 25.5431C18.8567 25.3397 18.9096 25.1183 18.9153 24.8932C18.9211 24.6681 18.8795 24.4443 18.7932 24.2363C18.707 24.0283 18.578 23.8407 18.4146 23.6857C18.2513 23.5307 18.0572 23.4117 17.845 23.3364C17.5005 23.2134 17.2026 22.9869 16.9919 22.6879C16.7812 22.389 16.6681 22.0322 16.6681 21.6664C16.6681 21.3007 16.7812 20.9439 16.9919 20.6449C17.2026 20.346 17.5005 20.1195 17.845 19.9964C18.057 19.921 18.251 19.8019 18.4142 19.6468C18.5774 19.4918 18.7062 19.3042 18.7924 19.0963C18.8787 18.8883 18.9203 18.6646 18.9147 18.4396C18.9091 18.2145 18.8564 17.9932 18.76 17.7898C18.603 17.4587 18.5522 17.0873 18.6146 16.7263C18.677 16.3652 18.8494 16.0323 19.1083 15.7731C19.3701 15.5173 19.7038 15.3473 20.0647 15.286C20.4256 15.2246 20.7966 15.2748 21.1283 15.4298C21.3315 15.5253 21.5526 15.5772 21.7771 15.5821C22.0016 15.587 22.2247 15.5449 22.4319 15.4584C22.6392 15.3719 22.826 15.2429 22.9804 15.0798C23.1348 14.9167 23.2533 14.7231 23.3283 14.5114C23.451 14.1664 23.6775 13.8679 23.9766 13.6567C24.2758 13.4456 24.633 13.3323 24.9991 13.3323C25.3653 13.3323 25.7225 13.4456 26.0216 13.6567C26.3208 13.8679 26.5473 14.1664 26.67 14.5114ZM28.3333 36.1781V27.7781C27.9784 27.7331 27.6189 27.8123 27.3158 28.0022C27.0126 28.192 26.7844 28.4809 26.67 28.8198C26.5469 29.1642 26.3204 29.4622 26.0215 29.6728C25.7225 29.8835 25.3657 29.9966 25 29.9966C24.6342 29.9966 24.2774 29.8835 23.9785 29.6728C23.6795 29.4622 23.453 29.1642 23.33 28.8198C23.2155 28.4809 22.9873 28.192 22.6842 28.0022C22.381 27.8123 22.0215 27.7331 21.6666 27.7781V36.1764L25 33.3564L28.3333 36.1781ZM25.1 28.2631C25.3267 27.6254 25.7453 27.0735 26.2983 26.6832C26.8512 26.2928 27.5115 26.0832 28.1883 26.0831C28.6894 26.0885 29.1837 26.2004 29.6383 26.4114L29.735 26.2598C29.538 25.846 29.4301 25.3955 29.4182 24.9374C29.4063 24.4792 29.4908 24.0237 29.6662 23.6003C29.8415 23.1769 30.1038 22.7951 30.436 22.4794C30.7683 22.1638 31.1631 21.9215 31.595 21.7681V21.5648C31.1634 21.411 30.769 21.1684 30.437 20.8527C30.105 20.537 29.8429 20.1552 29.6677 19.7319C29.4924 19.3087 29.4078 18.8533 29.4194 18.3954C29.431 17.9374 29.5385 17.487 29.735 17.0731L29.59 16.9331C29.1761 17.1295 28.7257 17.2369 28.2678 17.2484C27.8098 17.2599 27.3546 17.1751 26.9314 16.9997C26.5083 16.8242 26.1266 16.562 25.8112 16.2298C25.4957 15.8977 25.2534 15.5031 25.1 15.0714H24.9C24.7465 15.5031 24.5042 15.8977 24.1888 16.2298C23.8733 16.562 23.4917 16.8242 23.0685 16.9997C22.6454 17.1751 22.1901 17.2599 21.7322 17.2484C21.2742 17.2369 20.8238 17.1295 20.41 16.9331L20.265 17.0731C20.4614 17.487 20.5689 17.9374 20.5805 18.3954C20.5921 18.8533 20.5075 19.3087 20.3323 19.7319C20.157 20.1552 19.8949 20.537 19.5629 20.8527C19.231 21.1684 18.8365 21.411 18.405 21.5648V21.7681C18.8368 21.9215 19.2316 22.1638 19.5639 22.4794C19.8962 22.7951 20.1584 23.1769 20.3338 23.6003C20.5091 24.0237 20.5936 24.4792 20.5817 24.9374C20.5699 25.3955 20.462 25.846 20.265 26.2598L20.3616 26.4098C20.8163 26.1993 21.3106 26.088 21.8116 26.0831C22.4885 26.0832 23.1487 26.2928 23.7017 26.6832C24.2546 27.0735 24.6732 27.6254 24.9 28.2631L25 28.3331L25.1 28.2631Z" fill="#2F6BFF"/>
</svg>
<svg width="36" height="40" viewBox="0 0 36 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M34.0417 20C33.655 20 33.284 20.1405 33.0105 20.3905C32.737 20.6406 32.5834 20.9797 32.5834 21.3333V25.3333C32.5834 25.687 32.737 26.0261 33.0105 26.2762C33.284 26.5262 33.655 26.6667 34.0417 26.6667C34.4285 26.6667 34.7994 26.5262 35.0729 26.2762C35.3464 26.0261 35.5001 25.687 35.5001 25.3333V21.3333C35.5001 20.9797 35.3464 20.6406 35.0729 20.3905C34.7994 20.1405 34.4285 20 34.0417 20ZM34.8584 14.8933C34.7773 14.8426 34.689 14.8022 34.5959 14.7733C34.5089 14.7332 34.4153 14.7062 34.3188 14.6933C34.0845 14.6504 33.8423 14.6608 33.6134 14.7236C33.3846 14.7864 33.1761 14.8996 33.0063 15.0533C32.8712 15.1779 32.7642 15.3257 32.6916 15.4881C32.6191 15.6506 32.5823 15.8245 32.5834 16C32.5834 16.3536 32.737 16.6928 33.0105 16.9428C33.284 17.1929 33.655 17.3333 34.0417 17.3333C34.4285 17.3333 34.7994 17.1929 35.0729 16.9428C35.3464 16.6928 35.5001 16.3536 35.5001 16C35.4947 15.647 35.3436 15.3089 35.0771 15.0533L34.8584 14.8933Z" fill="#2F6BFF"/>
<path d="M31.125 4H29.6667C29.6667 2.93913 29.2057 1.92172 28.3853 1.17157C27.5648 0.421427 26.452 0 25.2917 0H10.7083C9.54801 0 8.43521 0.421427 7.61474 1.17157C6.79427 1.92172 6.33333 2.93913 6.33333 4H4.875C3.73461 3.99963 2.63913 4.40638 1.82194 5.13361C1.00474 5.86084 0.53041 6.85106 0.5 7.89333V36.1067C0.53041 37.1489 1.00474 38.1392 1.82194 38.8664C2.63913 39.5936 3.73461 40.0004 4.875 40H31.125C32.2654 40.0004 33.3609 39.5936 34.1781 38.8664C34.9953 38.1392 35.4696 37.1489 35.5 36.1067V29.3333C35.5 28.9797 35.3464 28.6406 35.0729 28.3905C34.7994 28.1405 34.4284 28 34.0417 28C33.6549 28 33.284 28.1405 33.0105 28.3905C32.737 28.6406 32.5833 28.9797 32.5833 29.3333V36.1067C32.5539 36.4415 32.3874 36.7538 32.117 36.9812C31.8467 37.2087 31.4924 37.3344 31.125 37.3333H4.875C4.50762 37.3344 4.15332 37.2087 3.88295 36.9812C3.61258 36.7538 3.44606 36.4415 3.41667 36.1067V7.89333C3.44606 7.55851 3.61258 7.24617 3.88295 7.01875C4.15332 6.79133 4.50762 6.66559 4.875 6.66667H6.33333C6.33333 7.72753 6.79427 8.74495 7.61474 9.49509C8.43521 10.2452 9.54801 10.6667 10.7083 10.6667H25.2917C26.452 10.6667 27.5648 10.2452 28.3853 9.49509C29.2057 8.74495 29.6667 7.72753 29.6667 6.66667H31.125C31.4924 6.66559 31.8467 6.79133 32.117 7.01875C32.3874 7.24617 32.5539 7.55851 32.5833 7.89333V10.6667C32.5833 11.0203 32.737 11.3594 33.0105 11.6095C33.284 11.8595 33.6549 12 34.0417 12C34.4284 12 34.7994 11.8595 35.0729 11.6095C35.3464 11.3594 35.5 11.0203 35.5 10.6667V7.89333C35.4696 6.85106 34.9953 5.86084 34.1781 5.13361C33.3609 4.40638 32.2654 3.99963 31.125 4ZM26.75 6.66667C26.75 7.02029 26.5964 7.35943 26.3229 7.60948C26.0494 7.85952 25.6784 8 25.2917 8H10.7083C10.3216 8 9.95063 7.85952 9.67714 7.60948C9.40365 7.35943 9.25 7.02029 9.25 6.66667V4C9.25 3.64638 9.40365 3.30724 9.67714 3.05719C9.95063 2.80714 10.3216 2.66667 10.7083 2.66667H25.2917C25.6784 2.66667 26.0494 2.80714 26.3229 3.05719C26.5964 3.30724 26.75 3.64638 26.75 4V6.66667Z" fill="#2F6BFF"/>
<path d="M27.1292 16.44L15.0833 28.7333L8.82708 23.0533C8.5481 22.8349 8.18924 22.7207 7.82221 22.7337C7.45519 22.7467 7.10702 22.8858 6.8473 23.1232C6.58758 23.3607 6.43543 23.679 6.42125 24.0146C6.40708 24.3502 6.53192 24.6783 6.77083 24.9333L14.0625 31.6C14.3318 31.8512 14.6987 31.995 15.0833 32C15.2819 31.9959 15.4775 31.9547 15.658 31.8791C15.8386 31.8034 16.0003 31.6948 16.1333 31.56L29.2583 18.2267C29.5175 17.9632 29.6515 17.6164 29.631 17.2626C29.6105 16.9088 29.4371 16.5769 29.149 16.34C28.8608 16.1031 28.4815 15.9805 28.0945 15.9992C27.7075 16.018 27.3446 16.1765 27.0854 16.44H27.1292Z" fill="#2F6BFF"/>
</svg>
<svg width="none" height="none" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17.1917 17.1569L22 22M19.7778 10.8889C19.7778 15.7981 15.7981 19.7778 10.8889 19.7778C5.97969 19.7778 2 15.7981 2 10.8889C2 5.97969 5.97969 2 10.8889 2C15.7981 2 19.7778 5.97969 19.7778 10.8889Z" stroke="none" stroke-width="none" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<svg width="38" height="41" viewBox="0 0 38 41" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.2223 20.5001H13.1667M26.7778 20.5001H19.0001" stroke="#2F6BFF" stroke-width="4" stroke-linecap="round"/>
<path d="M26.7778 12.7217H24.8334M19.0001 12.7217H11.2223" stroke="#2F6BFF" stroke-width="4" stroke-linecap="round"/>
<path d="M11.2223 28.2775H20.9445" stroke="#2F6BFF" stroke-width="4" stroke-linecap="round"/>
<path d="M1.5 24.3889V16.6111C1.5 9.27813 1.5 5.61167 3.77805 3.33359C6.05612 1.05554 9.72259 1.05554 17.0556 1.05554H20.9444C28.2773 1.05554 31.944 1.05554 34.2219 3.33359C35.492 4.60369 36.0539 6.30533 36.3026 8.83332M36.5 16.6111V24.3889C36.5 31.7218 36.5 35.3884 34.2219 37.6663C31.944 39.9444 28.2773 39.9444 20.9444 39.9444H17.0556C9.72259 39.9444 6.05612 39.9444 3.77805 37.6663C2.50798 36.3962 1.946 34.6946 1.69734 32.1667" stroke="#2F6BFF" stroke-width="2" stroke-linecap="round"/>
</svg>
<svg width="36" height="37" viewBox="0 0 36 37" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M33.1437 30.0397C33.1437 30.0259 33.2401 26.9461 34.7214 25.4717C34.9833 25.2099 34.9901 24.7828 34.8317 24.4452C34.6663 24.1076 34.3287 23.894 33.9567 23.894H7.17617C3.81397 23.894 1.07184 26.6292 1.07184 29.9983C1.07184 33.3605 3.80708 36.1026 7.17617 36.1026H33.9567C34.3287 36.1026 34.6663 35.8891 34.8317 35.5515C34.997 35.2139 34.9488 34.8211 34.7214 34.5249C33.6742 33.2089 33.1507 31.6243 33.1437 30.0397ZM7.44487 30.9698H31.249C31.3661 32.0652 31.6762 33.1469 32.186 34.1528H7.17617C4.88188 34.1528 3.01475 32.2857 3.01475 29.9914C3.01475 27.6971 4.88188 25.83 7.17617 25.83H32.186C31.6762 26.8428 31.3661 27.9176 31.249 29.0131H7.44487C6.90747 29.0131 6.47341 29.4471 6.47341 29.9845C6.47341 30.5357 6.90747 30.9698 7.44487 30.9698Z" fill="#2F6BFF"/>
<path d="M34.7972 5.64611L18.2687 0.93351C18.0965 0.885282 17.9104 0.885282 17.7382 0.93351L1.20276 5.64611C0.78937 5.77012 0.5 6.14906 0.5 6.58312C0.5 7.01717 0.78937 7.39611 1.20276 7.52012L7.31398 9.26324V20.7347C7.31398 21.0861 7.50689 21.4099 7.81004 21.5821C8.12008 21.7544 8.49213 21.7475 8.79528 21.5615C11.8612 19.6806 14.9547 18.7298 18 18.7298C21.0453 18.7298 24.1319 19.6806 27.2047 21.5615C27.3632 21.6579 27.8317 21.7819 28.19 21.5821C28.5 21.4099 28.686 21.0861 28.686 20.7347V9.26324L31.9035 8.3469V13.7347C31.9035 14.2721 32.3376 14.7061 32.875 14.7061C33.4124 14.7061 33.8465 14.2721 33.8465 13.7347V7.78883L34.7972 7.52012C35.2175 7.403 35.5 7.01717 35.5 6.58312C35.5 6.14906 35.2106 5.77012 34.7972 5.64611ZM9.26378 19.0674V9.82131L17.7313 12.2396C17.9035 12.2878 18.0896 12.2878 18.2618 12.2396L26.7293 9.82131V19.0639C23.831 17.5505 20.9052 16.7869 18 16.7869C15.0925 16.7869 12.1575 17.5516 9.26378 19.0674ZM18 10.2898L5.01968 6.58312L18 2.88331L30.9803 6.58312L18 10.2898Z" fill="#2F6BFF"/>
</svg>
<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M27.1244 8.53633L25.2745 6.03968C25.1555 5.88122 25.0006 5.75329 24.8224 5.66646C24.6442 5.57964 24.4478 5.53641 24.2496 5.54035H12.0004C11.8198 5.54033 11.6414 5.57938 11.4774 5.65481C11.3135 5.73023 11.1678 5.84023 11.0505 5.97725L8.90058 8.47392C8.71596 8.70353 8.61856 8.99093 8.6256 9.28534V18.3857C8.6289 20.5598 9.49516 22.644 11.0345 24.1813C12.5739 25.7188 14.6606 26.5839 16.8376 26.5872H19.1624C21.3393 26.5839 23.4262 25.7188 24.9656 24.1813C26.5048 22.644 27.371 20.5598 27.3744 18.3857V9.28534C27.3744 9.01523 27.2867 8.75241 27.1244 8.53633Z" fill="white"/>
<path d="M35.2239 33.4777L30.2242 27.236C30.0792 27.0514 29.885 26.9112 29.664 26.8318C29.4429 26.7523 29.2039 26.7367 28.9743 26.7866L18 29.2583L7.0257 26.7617C6.79613 26.7117 6.55708 26.7273 6.33601 26.8068C6.11494 26.8862 5.92076 27.0264 5.77578 27.2111L0.776111 33.4527C0.631033 33.6355 0.540089 33.855 0.513578 34.0867C0.487067 34.3183 0.526052 34.5529 0.626121 34.7635C0.723814 34.9805 0.881617 35.1653 1.08092 35.2957C1.28023 35.4262 1.51274 35.4971 1.75105 35.5H34.2489C34.4845 35.4993 34.7152 35.4322 34.9141 35.3062C35.1131 35.1804 35.2725 35.0009 35.3739 34.7884C35.4739 34.5778 35.5129 34.3433 35.4864 34.1117C35.4599 33.88 35.369 33.6604 35.2239 33.4777Z" fill="#2F6BFF"/>
<path d="M34.6114 5.54014L18.3625 0.546822C18.1249 0.484393 17.8751 0.484393 17.6375 0.546822L1.38858 5.54014C1.12287 5.62028 0.89144 5.78647 0.73075 6.01249C0.570073 6.23851 0.489291 6.51152 0.50114 6.78848C0.490766 7.06865 0.575135 7.34417 0.740662 7.57063C0.906176 7.79708 1.14322 7.96129 1.41358 8.03681L17.6625 12.5308C17.8825 12.5974 18.1175 12.5974 18.3375 12.5308L34.5864 8.03681C34.8568 7.96129 35.0938 7.79708 35.2594 7.57063C35.4249 7.34417 35.5092 7.06865 35.4989 6.78848C35.5107 6.51152 35.4299 6.23851 35.2692 6.01249C35.1085 5.78647 34.8771 5.62028 34.6114 5.54014Z" fill="#2F6BFF"/>
<path d="M12.0254 5.54022C11.8449 5.54021 11.6664 5.57926 11.5024 5.65468C11.3384 5.73011 11.1928 5.84011 11.0755 5.97714L8.92559 8.4738C8.73197 8.69992 8.62559 8.98769 8.62561 9.28522V18.3855C8.62891 20.5598 9.49517 22.6439 11.0345 24.1813C12.5739 25.7186 14.6606 26.5838 16.8376 26.5871H18V5.54022H12.0254Z" fill="#2F6BFF"/>
<path d="M7.0257 26.7617C6.79613 26.7117 6.55708 26.7273 6.33601 26.8068C6.11494 26.8862 5.92076 27.0264 5.77578 27.2111L0.776111 33.4527C0.631033 33.6355 0.540089 33.855 0.513578 34.0867C0.487067 34.3183 0.526052 34.5529 0.626121 34.7635C0.723814 34.9805 0.881617 35.1653 1.08092 35.2957C1.28023 35.4262 1.51274 35.4971 1.75105 35.5H18V29.2583L7.0257 26.7617Z" fill="#2F6BFF"/>
<path d="M17.6375 0.546884L1.38858 5.54021C1.12287 5.62035 0.89144 5.78653 0.73075 6.01255C0.570073 6.23859 0.489291 6.5116 0.50114 6.78854C0.490766 7.06873 0.575135 7.34424 0.740662 7.57069C0.906176 7.79715 1.14322 7.96137 1.41358 8.03687L17.6625 12.5308C17.7743 12.5491 17.8883 12.5491 18 12.5308V0.546884C17.8799 0.527748 17.7576 0.527748 17.6375 0.546884Z" fill="#2F6BFF"/>
<path d="M1.41358 8.03681L8.62561 9.9842V18.3854C8.62891 20.5597 9.49517 22.6439 11.0345 24.1812C12.5739 25.7185 14.6606 26.5837 16.8376 26.587H19.1624C21.3394 26.5837 23.4261 25.7185 24.9655 24.1812C26.5048 22.6439 27.3711 20.5597 27.3744 18.3854V9.9842L34.5864 8.03681C34.8568 7.9613 35.0938 7.79708 35.2594 7.57063C35.4249 7.34417 35.5092 7.06865 35.4989 6.78848C35.5107 6.51152 35.4299 6.23851 35.2692 6.01249C35.1085 5.78647 34.8771 5.62028 34.6114 5.54015L18.3625 0.546822C18.1249 0.484393 17.8751 0.484393 17.6375 0.546822L1.38858 5.54015C1.12287 5.62028 0.89144 5.78647 0.73075 6.01249C0.570073 6.23851 0.489291 6.51152 0.50114 6.78848C0.490766 7.06865 0.575135 7.34417 0.740662 7.57063C0.906176 7.79708 1.14322 7.9613 1.41358 8.03681ZM24.8745 18.3854C24.8713 19.8975 24.2683 21.3466 23.1979 22.4158C22.1274 23.485 20.6763 24.0871 19.1624 24.0903H16.8376C15.3237 24.0871 13.8726 23.485 12.8021 22.4158C11.7317 21.3466 11.1287 19.8975 11.1254 18.3854V10.6707L17.6625 12.4808C17.8825 12.5475 18.1175 12.5475 18.3375 12.4808L24.8745 10.6707V18.3854ZM18 3.1059L29.7992 6.78848L18 9.9842L6.20077 6.78848L18 3.1059Z" fill="#2F6BFF"/>
<path d="M30.2242 27.236C30.0792 27.0514 29.885 26.9112 29.664 26.8318C29.4429 26.7523 29.2039 26.7367 28.9743 26.7866L18 29.2583L7.0257 26.7617C6.79613 26.7117 6.55708 26.7273 6.33601 26.8068C6.11494 26.8862 5.92076 27.0264 5.77578 27.2111L0.776111 33.4527C0.631033 33.6355 0.540089 33.855 0.513578 34.0867C0.487067 34.3183 0.526052 34.5529 0.626121 34.7635C0.723814 34.9805 0.881617 35.1653 1.08092 35.2957C1.28023 35.4262 1.51274 35.4971 1.75105 35.5H34.2489C34.4845 35.4993 34.7152 35.4322 34.9141 35.3062C35.1131 35.1804 35.2725 35.0009 35.3739 34.7884C35.4739 34.5778 35.5129 34.3433 35.4864 34.1117C35.4599 33.88 35.369 33.6604 35.2239 33.4777L30.2242 27.236ZM4.35088 33.0033L7.23819 29.3956L17.725 31.755C17.9064 31.7923 18.0935 31.7923 18.275 31.755L28.7618 29.4206L31.6491 33.0033H4.35088Z" fill="#2F6BFF"/>
</svg>
// AFTER: Đúng cách - import SVG như component
import ArrowBottomIcon from './icon/arrow_bottom_profile.svg';
import ArrowRightIcon from './icon/arrow_right_schedule.svg';
import BackIcon from './icon/back_header.svg';
import CalendarIcon from './icon/calendar_home.svg';
import CameraIcon from './icon/camera_profile.svg';
import DebtIcon from './icon/debt_home.svg';
import ExamScheduleIcon from './icon/exam_schedule_home.svg';
import ImageIcon from './icon/image_dialog.svg';
import JobIcon from './icon/job_home.svg';
import MedicalIcon from './icon/medical_home.svg';
import OutpatientRegistrationIcon from './icon/outpatient_registration_home.svg';
import PeopleIcon from './icon/people_bottom_tab_bar.svg';
import RFCertificateIcon from './icon/register_for_certificate.svg';
import RollCallIcon from './icon/roll_call_home.svg';
import SearchIcon from './icon/search_header.svg';
import StudyMaterialsIcon from './icon/study_materials_home.svg';
import TrainingPointIcon from './icon/training_point_home.svg';
import TrainingProgramIcon from './icon/training_program_home.svg';
import QrCodeIconButton from './icon/qr_code_button.svg'
import Tick from './icon/tick.png';
import Plus from './icon/icon_plus.svg';
import Date from './icon/date.svg'
import ArrowLeftBlack from './icon/back_header_custom.svg';
import DateDrawer from './icon/day.svg';
import Date3Drawer from './icon/filter3day.svg';
import Menu from './icon/menu.svg'
import FilterWeek from './icon/filter_week.svg';
import FilterMonth from './icon/icon_month.svg';
const images = { const images = {
iconWarn: require('./images/iconWarn.png'), icPlus: Plus,
iconSuccess: require('./images/iconSuccess.png'), igBackgroundSlider:require('./images/background_slider_home.jpg'),
iconError: require('./images/iconError.png'), igLogo:require('./images/logo.png'),
iconCamera: require('./images/iconCamera.png'), icHomeSel:require('./icon/home_tab_navigation.png'),
icHome: require('./images/icHome.png'), icHomeUnSel:require('./icon/home_tab_navigation_unsel.png'),
icAccount: require('./images/icAccount.png'), icELearing:require('./icon/e_learning_navigation.png'),
bg_cannot_connect: require('./images/bg_cannot_connect.png'), icELearingUnSel:require('./icon/e_learning_navigation_unsel.png'),
icGallery: require('./images/icGallery.png'), icQrCodeUnSel:require('./icon/qr_code_unsel.png'),
icQrCode:require('./icon/qr_code_navigation.png'),
icNotification:require('./icon/notificaton.png'),
icNotificationUnSel:require('./icon/notification_unsel.png'),
icProfile:require('./icon/profile_navigation.png'),
icProfileUn:require('./icon/profile_navigation_unsel.png'),
igProfileDemo:require('./images/image.png'),
icImageDownload:require('./images/icImageDownload.png'),
ic3Date:Date3Drawer,
icDateDrawer:DateDrawer,
icMenu:Menu,
icArrowLeftBlack:ArrowLeftBlack,
icWeek:FilterWeek,
icMonth:FilterMonth,
icArrowBottom: ArrowBottomIcon,
icArrowRight: ArrowRightIcon,
icBack: BackIcon,
icCalendar: CalendarIcon,
icCamera: CameraIcon,
icDebt: DebtIcon,
icExamSchedule: ExamScheduleIcon,
icImage: ImageIcon,
icJob: JobIcon,
icMedical: MedicalIcon,
icOutpatientRegistration: OutpatientRegistrationIcon,
icPeople: PeopleIcon,
icRFCertificate: RFCertificateIcon,
icRollCall: RollCallIcon,
icSearch: SearchIcon,
icStudyMaterials: StudyMaterialsIcon,
icTrainingPoint: TrainingPointIcon,
icTrainingProgram: TrainingProgramIcon,
icQrCodeButton: QrCodeIconButton,
icTick: Tick,
icDate: Date
}; };
export default images; export default images;
import React, {Component} from 'react'; import React, { useState, useCallback } from 'react';
import { import {
StyleSheet, StyleSheet,
TouchableOpacity, TouchableOpacity,
View, View,
Text, Text,
ImageBackground, ActivityIndicator,
Animated,
Pressable,
Platform,
} from 'react-native'; } from 'react-native';
import R from '../assets/R';
import {colors, sizes} from '../assets/theme';
import {
getFontSize,
getFontXD,
getWidth,
HEIGHT,
WIDTH,
} from '../config/Functions';
const Button = props => { /**
const {title, onPress, containerStyle, txtStyle, backgroundColor} = props; * FEATURE: Fully Customizable Button Component - KHÔNG CÓ GIÁ TRỊ CỐ ĐỊNH
*
* Tất cả props đều optional và có thể override hoàn toàn
* Không có default styles cứng, chỉ có fallback khi không truyền props
*/
return ( const Button = ({
<TouchableOpacity // FUNCTIONALITY: Text & Action props
style={[ title,
{ onPress,
height: HEIGHT(40),
justifyContent: 'center', // UI/UX: Icon props
alignItems: 'center', icon: IconComponent,
backgroundColor: '#F2B60D', iconPosition = 'left',
marginHorizontal: WIDTH(15), iconSize,
borderRadius: HEIGHT(6), iconColor,
marginTop: HEIGHT(30),
shadowColor: '#000', // UI/UX: Icon Spacing props - TỰ TÙY CHỈNH HOÀN TOÀN
shadowOffset: { iconSpacing, // Áp dụng cho tất cả directions
width: 0, iconSpacingHorizontal, // Chỉ cho left/right
height: 1, iconSpacingVertical, // Chỉ cho top/bottom
}, iconSpacingLeft, // Chỉ khi icon ở left
shadowOpacity: 0.22, iconSpacingRight, // Chỉ khi icon ở right
shadowRadius: 2.22, iconSpacingTop, // Chỉ khi icon ở top
elevation: 3, iconSpacingBottom, // Chỉ khi icon ở bottom
},
{...containerStyle}, // UI/UX: Dimension props - HOÀN TOÀN TÙY CHỈNH
]} width,
disabled={props.disabled} height,
onPress={onPress}> paddingHorizontal,
<View paddingVertical,
style={{ paddingTop,
flex: 1, paddingBottom,
justifyContent: 'center', paddingLeft,
alignItems: 'center', paddingRight,
}}> margin,
<Text marginHorizontal,
style={[ marginVertical,
{ marginTop,
fontSize: getFontSize(18), marginBottom,
color: R.colors.white, marginLeft,
fontWeight: 'bold', marginRight,
},
{...txtStyle}, // UI/UX: Color props - HOÀN TOÀN TÙY CHỈNH
]}> backgroundColor,
{title} pressedBackgroundColor,
</Text> disabledBackgroundColor,
textColor,
pressedTextColor,
disabledTextColor,
// UI/UX: Border props - HOÀN TOÀN TÙY CHỈNH
borderRadius,
borderTopLeftRadius,
borderTopRightRadius,
borderBottomLeftRadius,
borderBottomRightRadius,
borderWidth,
borderTopWidth,
borderBottomWidth,
borderLeftWidth,
borderRightWidth,
borderColor,
borderTopColor,
borderBottomColor,
borderLeftColor,
borderRightColor,
borderStyle,
// UI/UX: Text props - HOÀN TOÀN TÙY CHỈNH
fontSize,
fontWeight,
fontFamily,
fontStyle,
textAlign,
textDecorationLine,
textDecorationStyle,
textDecorationColor,
letterSpacing,
lineHeight,
textTransform,
numberOfLines,
// UI/UX: Text Padding props - CHỈ CHO TEXT
textPaddingHorizontal,
textPaddingVertical,
textPaddingTop,
textPaddingBottom,
textPaddingLeft,
textPaddingRight,
// STATE: Interaction props
disabled = false,
loading = false,
// UI/UX: Layout props - HOÀN TOÀN TÙY CHỈNH
justifyContent,
alignItems,
alignSelf,
flexDirection,
flex,
flexWrap,
flexGrow,
flexShrink,
flexBasis,
// FEATURE: Inline layout props - CHO BUTTONS CÙNG DÒNG
inline = false, // Tự động set flex để buttons nằm cùng dòng
inlineSpacing, // Margin giữa các inline buttons
// UI/UX: Position props - HOÀN TOÀN TÙY CHỈNH
position,
top,
bottom,
left,
right,
zIndex,
// UI/UX: Shadow props - HOÀN TOÀN TÙY CHỈNH
shadowColor,
shadowOffset,
shadowOpacity,
shadowRadius,
elevation,
// UI/UX: Background props - HOÀN TOÀN TÙY CHỈNH
opacity,
transform,
// PERFORMANCE: Style override props
containerStyle = {},
textStyle = {},
iconStyle = {},
iconContainerStyle = {},
contentContainerStyle = {},
loadingStyle = {},
// FEATURE: Interaction props - HOÀN TOÀN TÙY CHỈNH
activeOpacity,
delayPressIn,
delayPressOut,
delayLongPress,
onPressIn,
onPressOut,
onLongPress,
// FEATURE: Animation props - HOÀN TOÀN TÙY CHỈNH
enableAnimation = true,
animationScale,
animationOpacity,
animationDuration,
animationDelay,
// FEATURE: Platform specific props
useRipple = false,
rippleColor,
rippleBorderless = false,
// FEATURE: Loading props - HOÀN TOÀN TÙY CHỈNH
loadingSize,
loadingColor,
hideTextWhenLoading = false,
hideIconWhenLoading = false,
// FEATURE: Accessibility props
accessible,
accessibilityLabel,
accessibilityHint,
accessibilityRole,
testID,
// CONFIG: Custom props
hitSlop,
pressRetentionOffset,
}) => {
// STATE: Animation và interaction state
const [isPressed, setIsPressed] = useState(false);
const [animatedValue] = useState(new Animated.Value(1));
// FUNCTIONALITY: Handle press animations - TÙY CHỈNH HOÀN TOÀN
const handlePressIn = useCallback((event) => {
setIsPressed(true);
onPressIn && onPressIn(event);
if (enableAnimation && !disabled && !loading) {
const animations = [];
if (animationScale !== undefined) {
animations.push(
Animated.timing(animatedValue, {
toValue: animationScale,
duration: animationDuration || 150,
delay: animationDelay || 0,
useNativeDriver: true,
})
);
}
if (animations.length > 0) {
Animated.parallel(animations).start();
}
}
}, [
onPressIn, enableAnimation, disabled, loading, animationScale,
animationDuration, animationDelay, animatedValue
]);
// FUNCTIONALITY: Handle press release - TÙY CHỈNH HOÀN TOÀN
const handlePressOut = useCallback((event) => {
setIsPressed(false);
onPressOut && onPressOut(event);
if (enableAnimation && !disabled && !loading) {
const animations = [];
if (animationScale !== undefined) {
animations.push(
Animated.timing(animatedValue, {
toValue: 1,
duration: animationDuration || 150,
delay: animationDelay || 0,
useNativeDriver: true,
})
);
}
if (animations.length > 0) {
Animated.parallel(animations).start();
}
}
}, [
onPressOut, enableAnimation, disabled, loading, animationScale,
animationDuration, animationDelay, animatedValue
]);
// FUNCTIONALITY: Handle press action
const handlePress = useCallback((event) => {
if (disabled || loading) return;
onPress && onPress(event);
}, [disabled, loading, onPress]);
// FUNCTIONALITY: Handle long press
const handleLongPress = useCallback((event) => {
if (disabled || loading) return;
onLongPress && onLongPress(event);
}, [disabled, loading, onLongPress]);
// FUNCTIONALITY: Calculate icon spacing - LOGIC TỰ TÙY CHỈNH
const getIconSpacing = useCallback(() => {
// FEATURE: Priority system cho spacing
// 1. Specific position spacing (highest priority)
// 2. Horizontal/Vertical spacing
// 3. General iconSpacing
// 4. No spacing (0)
let horizontal = 0;
let vertical = 0;
// FUNCTIONALITY: Horizontal spacing (left/right positions)
if (iconPosition === 'left' || iconPosition === 'right') {
if (iconPosition === 'left' && iconSpacingLeft !== undefined) {
horizontal = iconSpacingLeft;
} else if (iconPosition === 'right' && iconSpacingRight !== undefined) {
horizontal = iconSpacingRight;
} else if (iconSpacingHorizontal !== undefined) {
horizontal = iconSpacingHorizontal;
} else if (iconSpacing !== undefined) {
horizontal = iconSpacing;
}
}
// FUNCTIONALITY: Vertical spacing (top/bottom positions)
if (iconPosition === 'top' || iconPosition === 'bottom') {
if (iconPosition === 'top' && iconSpacingTop !== undefined) {
vertical = iconSpacingTop;
} else if (iconPosition === 'bottom' && iconSpacingBottom !== undefined) {
vertical = iconSpacingBottom;
} else if (iconSpacingVertical !== undefined) {
vertical = iconSpacingVertical;
} else if (iconSpacing !== undefined) {
vertical = iconSpacing;
}
}
return { horizontal, vertical };
}, [
iconPosition, iconSpacing, iconSpacingHorizontal, iconSpacingVertical,
iconSpacingLeft, iconSpacingRight, iconSpacingTop, iconSpacingBottom
]);
// UI/UX: Build container styles - HOÀN TOÀN TÙY CHỈNH
const buildContainerStyles = useCallback(() => {
const styles = {};
// FUNCTIONALITY: Dimensions - chỉ set khi có value
if (width !== undefined) styles.width = width;
if (height !== undefined) styles.height = height;
// FUNCTIONALITY: Padding - chỉ set khi có value
if (paddingHorizontal !== undefined) {
styles.paddingLeft = paddingHorizontal;
styles.paddingRight = paddingHorizontal;
}
if (paddingVertical !== undefined) {
styles.paddingTop = paddingVertical;
styles.paddingBottom = paddingVertical;
}
if (paddingTop !== undefined) styles.paddingTop = paddingTop;
if (paddingBottom !== undefined) styles.paddingBottom = paddingBottom;
if (paddingLeft !== undefined) styles.paddingLeft = paddingLeft;
if (paddingRight !== undefined) styles.paddingRight = paddingRight;
// FUNCTIONALITY: Margin - chỉ set khi có value
if (margin !== undefined) styles.margin = margin;
if (marginHorizontal !== undefined) {
styles.marginLeft = marginHorizontal;
styles.marginRight = marginHorizontal;
}
if (marginVertical !== undefined) {
styles.marginTop = marginVertical;
styles.marginBottom = marginVertical;
}
if (marginTop !== undefined) styles.marginTop = marginTop;
if (marginBottom !== undefined) styles.marginBottom = marginBottom;
if (marginLeft !== undefined) styles.marginLeft = marginLeft;
if (marginRight !== undefined) styles.marginRight = marginRight;
// FUNCTIONALITY: Background colors - với state logic
if (disabled && disabledBackgroundColor !== undefined) {
styles.backgroundColor = disabledBackgroundColor;
} else if (isPressed && pressedBackgroundColor !== undefined) {
styles.backgroundColor = pressedBackgroundColor;
} else if (backgroundColor !== undefined) {
styles.backgroundColor = backgroundColor;
}
// FUNCTIONALITY: Border radius - chỉ set khi có value
if (borderRadius !== undefined) styles.borderRadius = borderRadius;
if (borderTopLeftRadius !== undefined) styles.borderTopLeftRadius = borderTopLeftRadius;
if (borderTopRightRadius !== undefined) styles.borderTopRightRadius = borderTopRightRadius;
if (borderBottomLeftRadius !== undefined) styles.borderBottomLeftRadius = borderBottomLeftRadius;
if (borderBottomRightRadius !== undefined) styles.borderBottomRightRadius = borderBottomRightRadius;
// FUNCTIONALITY: Border width - chỉ set khi có value
if (borderWidth !== undefined) styles.borderWidth = borderWidth;
if (borderTopWidth !== undefined) styles.borderTopWidth = borderTopWidth;
if (borderBottomWidth !== undefined) styles.borderBottomWidth = borderBottomWidth;
if (borderLeftWidth !== undefined) styles.borderLeftWidth = borderLeftWidth;
if (borderRightWidth !== undefined) styles.borderRightWidth = borderRightWidth;
// FUNCTIONALITY: Border color - chỉ set khi có value
if (borderColor !== undefined) styles.borderColor = borderColor;
if (borderTopColor !== undefined) styles.borderTopColor = borderTopColor;
if (borderBottomColor !== undefined) styles.borderBottomColor = borderBottomColor;
if (borderLeftColor !== undefined) styles.borderLeftColor = borderLeftColor;
if (borderRightColor !== undefined) styles.borderRightColor = borderRightColor;
if (borderStyle !== undefined) styles.borderStyle = borderStyle;
// FUNCTIONALITY: Layout - chỉ set khi có value
if (justifyContent !== undefined) styles.justifyContent = justifyContent;
if (alignItems !== undefined) styles.alignItems = alignItems;
if (alignSelf !== undefined) styles.alignSelf = alignSelf;
if (flexDirection !== undefined) styles.flexDirection = flexDirection;
if (flex !== undefined) styles.flex = flex;
if (flexWrap !== undefined) styles.flexWrap = flexWrap;
if (flexGrow !== undefined) styles.flexGrow = flexGrow;
if (flexShrink !== undefined) styles.flexShrink = flexShrink;
if (flexBasis !== undefined) styles.flexBasis = flexBasis;
// FEATURE: Inline layout - buttons cùng dòng
if (inline) {
styles.flex = flex !== undefined ? flex : 1; // Default flex = 1 khi inline
if (inlineSpacing !== undefined) {
styles.marginHorizontal = inlineSpacing;
}
}
// FUNCTIONALITY: Position - chỉ set khi có value
if (position !== undefined) styles.position = position;
if (top !== undefined) styles.top = top;
if (bottom !== undefined) styles.bottom = bottom;
if (left !== undefined) styles.left = left;
if (right !== undefined) styles.right = right;
if (zIndex !== undefined) styles.zIndex = zIndex;
// FUNCTIONALITY: Shadow - chỉ set khi có value
if (shadowColor !== undefined) styles.shadowColor = shadowColor;
if (shadowOffset !== undefined) styles.shadowOffset = shadowOffset;
if (shadowOpacity !== undefined) styles.shadowOpacity = shadowOpacity;
if (shadowRadius !== undefined) styles.shadowRadius = shadowRadius;
if (elevation !== undefined) styles.elevation = elevation;
// FUNCTIONALITY: Transform & Opacity - chỉ set khi có value
if (opacity !== undefined) styles.opacity = opacity;
if (transform !== undefined) styles.transform = transform;
return styles;
}, [
width, height, paddingHorizontal, paddingVertical, paddingTop, paddingBottom,
paddingLeft, paddingRight, margin, marginHorizontal, marginVertical,
marginTop, marginBottom, marginLeft, marginRight, disabled, isPressed,
disabledBackgroundColor, pressedBackgroundColor, backgroundColor,
borderRadius, borderTopLeftRadius, borderTopRightRadius, borderBottomLeftRadius,
borderBottomRightRadius, borderWidth, borderTopWidth, borderBottomWidth,
borderLeftWidth, borderRightWidth, borderColor, borderTopColor, borderBottomColor,
borderLeftColor, borderRightColor, borderStyle, justifyContent, alignItems,
alignSelf, flexDirection, flex, flexWrap, flexGrow, flexShrink, flexBasis,
inline, inlineSpacing, position, top, bottom, left, right, zIndex, shadowColor,
shadowOffset, shadowOpacity, shadowRadius, elevation, opacity, transform
]);
// UI/UX: Build text styles - HOÀN TOÀN TÙY CHỈNH
const buildTextStyles = useCallback(() => {
const styles = {};
// FUNCTIONALITY: Text colors - với state logic
if (disabled && disabledTextColor !== undefined) {
styles.color = disabledTextColor;
} else if (isPressed && pressedTextColor !== undefined) {
styles.color = pressedTextColor;
} else if (textColor !== undefined) {
styles.color = textColor;
}
// FUNCTIONALITY: Text properties - chỉ set khi có value
if (fontSize !== undefined) styles.fontSize = fontSize;
if (fontWeight !== undefined) styles.fontWeight = fontWeight;
if (fontFamily !== undefined) styles.fontFamily = fontFamily;
if (fontStyle !== undefined) styles.fontStyle = fontStyle;
if (textAlign !== undefined) styles.textAlign = textAlign;
if (textDecorationLine !== undefined) styles.textDecorationLine = textDecorationLine;
if (textDecorationStyle !== undefined) styles.textDecorationStyle = textDecorationStyle;
if (textDecorationColor !== undefined) styles.textDecorationColor = textDecorationColor;
if (letterSpacing !== undefined) styles.letterSpacing = letterSpacing;
if (lineHeight !== undefined) styles.lineHeight = lineHeight;
if (textTransform !== undefined) styles.textTransform = textTransform;
// FEATURE: Text padding - chỉ cho text element
if (textPaddingHorizontal !== undefined) {
styles.paddingLeft = textPaddingHorizontal;
styles.paddingRight = textPaddingHorizontal;
}
if (textPaddingVertical !== undefined) {
styles.paddingTop = textPaddingVertical;
styles.paddingBottom = textPaddingVertical;
}
if (textPaddingTop !== undefined) styles.paddingTop = textPaddingTop;
if (textPaddingBottom !== undefined) styles.paddingBottom = textPaddingBottom;
if (textPaddingLeft !== undefined) styles.paddingLeft = textPaddingLeft;
if (textPaddingRight !== undefined) styles.paddingRight = textPaddingRight;
return styles;
}, [
disabled, isPressed, disabledTextColor, pressedTextColor, textColor,
fontSize, fontWeight, fontFamily, fontStyle, textAlign, textDecorationLine,
textDecorationStyle, textDecorationColor, letterSpacing, lineHeight, textTransform,
textPaddingHorizontal, textPaddingVertical, textPaddingTop, textPaddingBottom,
textPaddingLeft, textPaddingRight
]);
// UI/UX: Get current icon color - TÙY CHỈNH HOÀN TOÀN
const getCurrentIconColor = useCallback(() => {
if (disabled && disabledTextColor !== undefined) {
return disabledTextColor;
}
if (isPressed && pressedTextColor !== undefined) {
return pressedTextColor;
}
if (iconColor !== undefined) {
return iconColor;
}
if (textColor !== undefined) {
return textColor;
}
return undefined;
}, [disabled, isPressed, disabledTextColor, pressedTextColor, iconColor, textColor]);
// UI/UX: Build animation styles
const buildAnimationStyles = useCallback(() => {
const styles = {};
if (enableAnimation) {
const transformArray = [];
if (animationScale !== undefined) {
transformArray.push({ scale: animatedValue });
}
if (transformArray.length > 0) {
styles.transform = transformArray;
}
if (animationOpacity !== undefined) {
styles.opacity = animatedValue;
}
}
return styles;
}, [enableAnimation, animationScale, animationOpacity, animatedValue]);
// UI/UX: Render icon - HOÀN TOÀN TÙY CHỈNH
const renderIcon = useCallback(() => {
if (!IconComponent || (loading && hideIconWhenLoading)) return null;
const iconProps = {};
if (iconSize !== undefined) iconProps.size = iconSize;
const currentIconColor = getCurrentIconColor();
if (currentIconColor !== undefined) iconProps.color = currentIconColor;
// FEATURE: Icon container với proper alignment
const iconContainerStyles = [
{
// UI/UX: Đảm bảo icon align center
justifyContent: 'center',
alignItems: 'center',
},
iconContainerStyle
];
return (
<View style={iconContainerStyles}>
{typeof IconComponent === 'function' ? (
<IconComponent {...iconProps} style={iconStyle} />
) : (
React.cloneElement(IconComponent, {
...iconProps,
style: [IconComponent.props?.style, iconStyle]
})
)}
</View>
);
}, [
IconComponent, loading, hideIconWhenLoading, iconSize, getCurrentIconColor,
iconContainerStyle, iconStyle
]);
// UI/UX: Render text - HOÀN TOÀN TÙY CHỈNH
const renderText = useCallback(() => {
if (!title || (loading && hideTextWhenLoading)) return null;
return (
<Text
style={[buildTextStyles(), textStyle]}
numberOfLines={numberOfLines}
>
{title}
</Text>
);
}, [title, loading, hideTextWhenLoading, buildTextStyles, textStyle, numberOfLines]);
// UI/UX: Render loading - HOÀN TOÀN TÙY CHỈNH
const renderLoading = useCallback(() => {
if (!loading) return null;
const loadingProps = {};
if (loadingSize !== undefined) loadingProps.size = loadingSize;
if (loadingColor !== undefined) loadingProps.color = loadingColor;
return (
<ActivityIndicator
{...loadingProps}
style={loadingStyle}
/>
);
}, [loading, loadingSize, loadingColor, loadingStyle]);
// UI/UX: Render content based on icon position
const renderContent = useCallback(() => {
const iconElement = renderIcon();
const textElement = renderText();
const loadingElement = renderLoading();
const spacing = getIconSpacing();
// FUNCTIONALITY: Build content container style với proper alignment
const getContentContainerStyle = () => {
const baseStyle = {
// FEATURE: Default alignment cho icon + text
flexDirection: iconPosition === 'top' || iconPosition === 'bottom' ? 'column' : 'row',
justifyContent: 'center',
alignItems: 'center',
// PERFORMANCE: Đảm bảo content luôn center trong button
flex: 1,
};
return [baseStyle, contentContainerStyle];
};
// FUNCTIONALITY: Loading state với proper layout
if (loading) {
return (
<View style={getContentContainerStyle()}>
{loadingElement}
{!hideTextWhenLoading && textElement && (
<View style={{
marginLeft: iconPosition === 'left' || iconPosition === 'right' ? spacing.horizontal : 0,
marginTop: iconPosition === 'top' || iconPosition === 'bottom' ? spacing.vertical : 0,
}}>
{textElement}
</View>
)}
{!hideIconWhenLoading && iconElement}
</View>
);
}
// UI/UX: Render content theo icon position với CUSTOM spacing
const renderContentByPosition = () => {
// FEATURE: Dynamic spacing based on icon position & custom props
const getSpacingStyle = (elementType, position) => {
const style = {};
if (elementType === 'icon') {
switch (position) {
case 'left':
if (textElement) style.marginRight = spacing.horizontal;
break;
case 'right':
if (textElement) style.marginLeft = spacing.horizontal;
break;
case 'top':
if (textElement) style.marginBottom = spacing.vertical;
break;
case 'bottom':
if (textElement) style.marginTop = spacing.vertical;
break;
}
} else if (elementType === 'text') {
switch (position) {
case 'left':
if (iconElement) style.marginLeft = spacing.horizontal;
break;
case 'right':
if (iconElement) style.marginRight = spacing.horizontal;
break;
case 'top':
if (iconElement) style.marginTop = spacing.vertical;
break;
case 'bottom':
if (iconElement) style.marginBottom = spacing.vertical;
break;
}
}
return style;
};
switch (iconPosition) {
case 'top':
return (
<>
{iconElement && (
<View style={getSpacingStyle('icon', 'top')}>
{iconElement}
</View>
)}
{textElement && (
<View style={getSpacingStyle('text', 'top')}>
{textElement}
</View>
)}
</>
);
case 'bottom':
return (
<>
{textElement && (
<View style={getSpacingStyle('text', 'bottom')}>
{textElement}
</View>
)}
{iconElement && (
<View style={getSpacingStyle('icon', 'bottom')}>
{iconElement}
</View>
)}
</>
);
case 'right':
return (
<>
{textElement && (
<View style={getSpacingStyle('text', 'right')}>
{textElement}
</View>
)}
{iconElement && (
<View style={getSpacingStyle('icon', 'right')}>
{iconElement}
</View>
)}
</>
);
default: // left
return (
<>
{iconElement && (
<View style={getSpacingStyle('icon', 'left')}>
{iconElement}
</View>
)}
{textElement && (
<View style={getSpacingStyle('text', 'left')}>
{textElement}
</View>
)}
</>
);
}
};
return (
<View style={getContentContainerStyle()}>
{renderContentByPosition()}
</View> </View>
</TouchableOpacity> );
}, [
renderIcon, renderText, renderLoading, loading, iconPosition,
contentContainerStyle, hideTextWhenLoading, hideIconWhenLoading, getIconSpacing
]);
// PERFORMANCE: Build final styles
const containerStyles = [
buildContainerStyles(),
containerStyle,
];
const animationStyles = buildAnimationStyles();
// FEATURE: Common props cho TouchableOpacity/Pressable
const commonProps = {
onPress: handlePress,
onPressIn: handlePressIn,
onPressOut: handlePressOut,
onLongPress: onLongPress,
disabled: disabled || loading,
delayPressIn,
delayPressOut,
delayLongPress,
hitSlop,
pressRetentionOffset,
accessible,
accessibilityLabel,
accessibilityHint,
accessibilityRole,
testID,
};
// PERFORMANCE: Sử dụng Pressable với ripple cho Android
if (useRipple && Platform.OS === 'android') {
const rippleConfig = {};
if (rippleColor !== undefined) rippleConfig.color = rippleColor;
if (rippleBorderless !== undefined) rippleConfig.borderless = rippleBorderless;
return (
<Animated.View style={animationStyles}>
<Pressable
style={containerStyles}
android_ripple={rippleConfig}
{...commonProps}
>
{renderContent()}
</Pressable>
</Animated.View>
);
}
// UI/UX: Default TouchableOpacity
return (
<Animated.View style={animationStyles}>
<TouchableOpacity
style={containerStyles}
activeOpacity={activeOpacity}
{...commonProps}
>
{renderContent()}
</TouchableOpacity>
</Animated.View>
); );
}; };
export default Button;
export default Button;
\ No newline at end of file
import React, { useCallback, useMemo, useState, useEffect } from 'react';
import {
TouchableOpacity,
View,
Text,
StyleSheet,
Animated,
Image
} from 'react-native';
import R from '../assets/R';
const IconTick = R.images.icTick;
// COMPONENT: TickIcon - Hiển thị biểu tượng tick với animation
const TickIcon = React.memo(({ visible, size, color }) => {
// PERFORMANCE: Chỉ tạo animatedValue một lần khi component mount
const animatedValue = useMemo(() => new Animated.Value(visible ? 1 : 0), []);
// FUNCTIONALITY: Animation spring khi visible thay đổi
useEffect(() => {
const animation = Animated.spring(animatedValue, {
toValue: visible ? 1 : 0,
useNativeDriver: true, // PERFORMANCE: Sử dụng native driver để tối ưu animation
tension: 300,
friction: 10,
});
animation.start();
// CLEANUP: Dọn dẹp animation khi component unmount
return () => {
animation.stop();
};
}, [visible, animatedValue]);
// UI/UX: Style cho image tick được tính toán một lần
const imageStyle = useMemo(() => ({
width: size * 0.6,
height: size * 0.6,
tintColor: color,
}), [size, color]);
return (
<Animated.View
style={{
transform: [{ scale: animatedValue }],
opacity: animatedValue,
justifyContent: 'center',
alignItems: 'center',
}}
>
<Image
source={IconTick}
style={imageStyle}
resizeMode="contain"
/>
</Animated.View>
);
});
// COMPONENT: Checkbox - Main component theo cấu trúc Button mới
const Checkbox = ({
// STATE: Checkbox value controls
value, // Controlled component value
defaultValue = false, // Uncontrolled component initial value
isCheck = false, // FEATURE: Mặc định bật tick ngay khi component mount
onValueChange,
// FUNCTIONALITY: Custom logic handlers
onPress, // FEATURE: Custom onPress handler để thực thi logic riêng
preventToggleOnPress = false, // FEATURE: Ngăn toggle tự động khi có custom onPress
// UI/UX: Checkbox appearance
size = 30,
borderRadius = 4,
borderColor = R.colors.black,
borderWidth = 1,
checkedColor = R.colors.main,
uncheckedColor = 'transparent',
tickColor = R.colors.white,
// FUNCTIONALITY: Label configuration
label,
labelPosition = 'right', // 'left' | 'right'
labelStyle,
labelSize = 16, // FEATURE: Kích thước font cho label
labelColor, // FEATURE: Màu sắc label (override default)
labelWeight = 'normal', // FEATURE: Font weight cho label ('normal', 'bold', '100', '200', ... '900')
labelOpacity, // FEATURE: Độ trong suốt label (override default)
labelSpacing = 5, // Khoảng cách giữa checkbox và label
labelNumberOfLines = 0, // FEATURE: Số dòng tối đa cho label (0 = unlimited)
labelEllipsizeMode = 'tail', // FEATURE: Cách cắt text khi quá dài ('head', 'middle', 'tail', 'clip')
labelLineHeight, // FEATURE: Chiều cao dòng cho label
labelLetterSpacing, // FEATURE: Khoảng cách giữa các ký tự
labelTextAlign = 'left', // FEATURE: Căn chỉnh text ('left', 'center', 'right')
labelTextDecorationLine, // FEATURE: Gạch chân/gạch ngang ('none', 'underline', 'line-through', 'underline line-through')
labelTextTransform, // FEATURE: Chuyển đổi chữ ('none', 'uppercase', 'lowercase', 'capitalize')
labelFontFamily, // FEATURE: Font family cho label
labelFontStyle = 'normal', // FEATURE: Font style ('normal', 'italic')
labelIncludeFontPadding = true, // FEATURE: Bao gồm font padding (Android only)
labelTextAlignVertical = 'auto', // FEATURE: Căn chỉnh vertical ('auto', 'top', 'bottom', 'center') - Android only
// UI/UX: Margin configuration
margin, // FEATURE: Margin cho toàn bộ component
marginTop, // FEATURE: Margin top riêng biệt
marginBottom, // FEATURE: Margin bottom riêng biệt
marginLeft, // FEATURE: Margin left riêng biệt
marginRight, // FEATURE: Margin right riêng biệt
marginHorizontal, // FEATURE: Margin horizontal (left + right)
marginVertical, // FEATURE: Margin vertical (top + bottom)
// STATE: Component state
disabled = false,
style,
testID,
}) => {
// STATE: Internal state cho uncontrolled component
// FEATURE: Ưu tiên isCheck > defaultValue để tự động bật tick
const [internalValue, setInternalValue] = useState(isCheck || defaultValue);
// FUNCTIONALITY: Xác định component là controlled hay uncontrolled
const isControlled = value !== undefined;
const currentValue = isControlled ? value : internalValue;
// FEATURE: Effect để xử lý isCheck khi component mount hoặc isCheck thay đổi
useEffect(() => {
// Chỉ áp dụng cho uncontrolled component
if (!isControlled && isCheck !== internalValue) {
setInternalValue(isCheck);
// FUNCTIONALITY: Trigger callback nếu có khi isCheck thay đổi
if (onValueChange && isCheck !== internalValue) {
onValueChange(isCheck);
}
}
}, [isCheck, isControlled, internalValue, onValueChange]);
// FUNCTIONALITY: Xử lý toggle checkbox - Đơn giản hóa logic
const handleToggle = useCallback(() => {
if (disabled) {
return;
}
const newValue = !currentValue;
// STATE: Cập nhật internal state nếu uncontrolled
if (!isControlled) {
setInternalValue(newValue);
}
// FUNCTIONALITY: Gọi callback nếu có - Wrap trong requestAnimationFrame để tránh conflict
if (onValueChange) {
requestAnimationFrame(() => {
onValueChange(newValue);
});
}
}, [currentValue, disabled, isControlled, onValueChange]);
// FUNCTIONALITY: Xử lý press event - Tối ưu để tránh multiple calls
const handlePress = useCallback(() => {
if (disabled) {
return;
}
// PERFORMANCE: Debounce để tránh multiple rapid taps
const now = Date.now();
if (handlePress.lastCall && (now - handlePress.lastCall) < 100) {
return;
}
handlePress.lastCall = now;
// FEATURE: Nếu có custom onPress handler
if (onPress) {
const checkboxInfo = {
currentValue,
isControlled,
toggleCheckbox: handleToggle,
};
try {
// FUNCTIONALITY: Thực thi custom logic
onPress(checkboxInfo);
// FUNCTIONALITY: Chỉ auto-toggle khi không bị prevent
if (!preventToggleOnPress) {
handleToggle();
}
} catch (error) {
console.error('Checkbox onPress error:', error);
}
return;
}
// FUNCTIONALITY: Không có custom onPress, toggle bình thường
handleToggle();
}, [
disabled,
onPress,
preventToggleOnPress,
currentValue,
isControlled,
handleToggle
]);
// UI/UX: Tính toán margin styles với priority
const marginStyles = useMemo(() => {
const styles = {};
// FEATURE: Áp dụng margin chung trước
if (margin !== undefined) {
styles.margin = margin;
}
// FEATURE: Áp dụng marginHorizontal và marginVertical
if (marginHorizontal !== undefined) {
styles.marginHorizontal = marginHorizontal;
}
if (marginVertical !== undefined) {
styles.marginVertical = marginVertical;
}
// FEATURE: Áp dụng margin riêng biệt (có priority cao nhất)
if (marginTop !== undefined) {
styles.marginTop = marginTop;
}
if (marginBottom !== undefined) {
styles.marginBottom = marginBottom;
}
if (marginLeft !== undefined) {
styles.marginLeft = marginLeft;
}
if (marginRight !== undefined) {
styles.marginRight = marginRight;
}
return styles;
}, [margin, marginTop, marginBottom, marginLeft, marginRight, marginHorizontal, marginVertical]);
// UI/UX: Style cho checkbox được memoize để tối ưu performance
const checkboxStyle = useMemo(() => ({
width: size,
height: size,
borderRadius,
borderWidth,
borderColor: currentValue ? checkedColor : borderColor,
backgroundColor: currentValue ? checkedColor : uncheckedColor,
opacity: disabled ? 0.5 : 1,
justifyContent: 'center',
alignItems: 'center',
}), [
size,
borderRadius,
borderWidth,
borderColor,
currentValue,
checkedColor,
uncheckedColor,
disabled,
]);
// UI/UX: Style cho label với các thuộc tính mở rộng
const defaultLabelStyle = useMemo(() => {
const baseStyle = {
fontSize: labelSize,
fontWeight: labelWeight,
fontStyle: labelFontStyle,
textAlign: labelTextAlign,
includeFontPadding: labelIncludeFontPadding,
textAlignVertical: labelTextAlignVertical,
};
// FEATURE: Màu sắc label với logic fallback
if (labelColor) {
baseStyle.color = labelColor;
} else {
baseStyle.color = disabled ? R.colors.gray : R.colors.black;
}
// FEATURE: Opacity label với logic fallback
if (labelOpacity !== undefined) {
baseStyle.opacity = labelOpacity;
} else {
baseStyle.opacity = disabled ? 0.5 : 1;
}
// FEATURE: Các thuộc tính typography tùy chọn
if (labelLineHeight) {
baseStyle.lineHeight = labelLineHeight;
}
if (labelLetterSpacing) {
baseStyle.letterSpacing = labelLetterSpacing;
}
if (labelTextDecorationLine) {
baseStyle.textDecorationLine = labelTextDecorationLine;
}
if (labelTextTransform) {
baseStyle.textTransform = labelTextTransform;
}
if (labelFontFamily) {
baseStyle.fontFamily = labelFontFamily;
}
return baseStyle;
}, [
labelSize,
labelColor,
labelWeight,
labelOpacity,
labelLineHeight,
labelLetterSpacing,
labelTextAlign,
labelTextDecorationLine,
labelTextTransform,
labelFontFamily,
labelFontStyle,
labelIncludeFontPadding,
labelTextAlignVertical,
disabled
]);
// UI/UX: Style cho container chính với margin
const containerStyle = useMemo(() => ({
flexDirection: labelPosition === 'right' ? 'row' : 'row-reverse',
alignItems: 'center',
...marginStyles, // FEATURE: Áp dụng margin styles
}), [labelPosition, marginStyles]);
// UI/UX: Style cho spacing giữa checkbox và label
const spacingStyle = useMemo(() => {
if (!label) return {};
return labelPosition === 'right'
? { marginLeft: labelSpacing }
: { marginRight: labelSpacing };
}, [label, labelPosition, labelSpacing]);
// FUNCTIONALITY: Render checkbox element
const renderCheckbox = () => (
<View style={checkboxStyle}>
{currentValue && (
<TickIcon
visible={currentValue}
size={size}
color={tickColor}
/>
)}
</View>
);
// FUNCTIONALITY: Render label element với các thuộc tính mở rộng
const renderLabel = () => {
if (!label) return null;
return (
<Text
style={[defaultLabelStyle, labelStyle, spacingStyle]}
numberOfLines={labelNumberOfLines}
ellipsizeMode={labelEllipsizeMode}
allowFontScaling={true} // FEATURE: Cho phép scale font theo system settings
adjustsFontSizeToFit={false} // FEATURE: Không auto-adjust font size
minimumFontScale={0.01} // FEATURE: Minimum scale factor khi adjustsFontSizeToFit = true
suppressHighlighting={true} // FEATURE: Ngăn highlight khi press (iOS)
selectable={false} // FEATURE: Không cho phép select text
textBreakStrategy="simple" // FEATURE: Text break strategy (Android)
>
{label}
</Text>
);
};
// UI/UX: Nếu không có label, render checkbox đơn giản với margin
if (!label) {
return (
<TouchableOpacity
style={[checkboxStyle, marginStyles, style]} // FEATURE: Áp dụng marginStyles
onPress={handlePress} // FEATURE: Sử dụng handlePress thay vì handleToggle
disabled={disabled}
activeOpacity={0.8}
testID={testID}
accessible={true}
accessibilityRole="checkbox"
accessibilityState={{ checked: currentValue, disabled }}
accessibilityHint="Tap to toggle checkbox"
>
{currentValue && (
<TickIcon
visible={currentValue}
size={size}
color={tickColor}
/>
)}
</TouchableOpacity>
);
}
// UI/UX: Render checkbox với label và margin
return (
<TouchableOpacity
style={[containerStyle, style]} // marginStyles đã được merge vào containerStyle
onPress={handlePress} // FEATURE: Sử dụng handlePress thay vì handleToggle
disabled={disabled}
activeOpacity={0.8}
testID={testID}
accessible={true}
accessibilityRole="checkbox"
accessibilityState={{ checked: currentValue, disabled }}
accessibilityLabel={typeof label === 'string' ? label : 'Checkbox'}
accessibilityHint="Tap to toggle checkbox"
>
{renderCheckbox()}
{renderLabel()}
</TouchableOpacity>
);
};
export default React.memo(Checkbox);
\ No newline at end of file
import React, { useState, useCallback, useRef } from "react";
import {
View,
Text,
TouchableOpacity,
FlatList,
Modal,
Animated,
Dimensions,
StyleSheet,
TextInput,
} from "react-native";
import R from "../../assets/R";
import { WIDTH, HEIGHT, getFontSize } from "../../config/Functions";
const DropdownItem = ({
item,
isSelected,
onSelect,
buildItemStyles,
buildItemTextStyles,
itemStyle,
itemTextStyle,
labelKey,
}) => {
const [isItemPressed, setIsItemPressed] = useState(false);
return (
<TouchableOpacity
style={[buildItemStyles(item, isSelected, isItemPressed), itemStyle]}
onPress={() => onSelect(item)}
onPressIn={() => setIsItemPressed(true)}
onPressOut={() => setIsItemPressed(false)}
activeOpacity={0.7}
>
<Text
style={[
buildItemTextStyles(item, isSelected, isItemPressed),
itemTextStyle,
]}
>
{item[labelKey]}
</Text>
</TouchableOpacity>
);
};
const DropdownSelect = ({
title,
titleStyle,
titleRequiredStyle,
data = [],
value,
onSelect,
placeholder = "Select an option",
searchable = false,
searchPlaceholder = "Search...",
titleColor, // Màu chữ title
titleFontSize, // Kích thước font title
titleFontWeight, // Độ đậm font title (normal, bold, 100-900)
titleFontFamily, // Font family title
titleFontStyle, // Font style title (normal, italic)
titleTextAlign, // Text align title (left, center, right)
titleLineHeight, // Line height title
titleLetterSpacing, // Letter spacing title
titleTextTransform, // Text transform (none, uppercase, lowercase, capitalize)
titleTextDecorationLine, // Text decoration (none, underline, line-through)
titleTextDecorationColor, // Màu decoration
titleTextDecorationStyle, // Style decoration (solid, double, dotted, dashed)
// TITLE: Title spacing props
titleMarginBottom, // Margin bottom của title
titleMarginTop, // Margin top của title
titleMarginLeft, // Margin left của title
titleMarginRight, // Margin right của title
titleMarginHorizontal, // Margin horizontal của title
titleMarginVertical, // Margin vertical của title
titlePaddingBottom, // Padding bottom của title
titlePaddingTop, // Padding top của title
titlePaddingLeft, // Padding left của title
titlePaddingRight, // Padding right của title
titlePaddingHorizontal, // Padding horizontal của title
titlePaddingVertical, // Padding vertical của title
// TITLE: Required symbol styling props
requiredSymbolColor, // Màu ký tự * required
requiredSymbolFontSize, // Kích thước font ký tự *
requiredSymbolFontWeight, // Độ đậm font ký tự *
requiredSymbolFontFamily, // Font family ký tự *
requiredSymbolText,
labelKey = "label",
valueKey = "value",
renderItem,
renderSelectedItem,
keyExtractor,
width,
height,
paddingHorizontal,
paddingVertical,
paddingTop,
paddingBottom,
paddingLeft,
paddingRight,
margin,
marginHorizontal,
marginVertical,
marginTop,
marginBottom,
marginLeft,
marginRight,
backgroundColor,
pressedBackgroundColor,
disabledBackgroundColor,
borderRadius,
borderTopLeftRadius,
borderTopRightRadius,
borderBottomLeftRadius,
borderBottomRightRadius,
borderWidth,
borderTopWidth,
borderBottomWidth,
borderLeftWidth,
borderRightWidth,
borderColor,
borderTopColor,
borderBottomColor,
borderLeftColor,
borderRightColor,
borderStyle,
textColor,
pressedTextColor,
disabledTextColor,
fontSize,
fontWeight,
fontFamily,
fontStyle,
textAlign,
lineHeight,
letterSpacing,
numberOfLines,
placeholderColor,
placeholderFontSize,
placeholderFontWeight,
placeholderFontFamily,
iconComponent,
iconSize,
iconColor,
pressedIconColor,
disabledIconColor,
iconPosition = "right",
iconSpacing,
iconSpacingHorizontal,
iconSpacingLeft,
iconSpacingRight,
dropdownMaxHeight,
dropdownBackgroundColor,
dropdownBorderRadius,
dropdownBorderWidth,
dropdownBorderColor,
dropdownShadowColor,
dropdownShadowOffset,
dropdownShadowOpacity,
dropdownShadowRadius,
dropdownElevation,
itemHeight,
itemPaddingHorizontal,
itemPaddingVertical,
itemBackgroundColor,
itemPressedBackgroundColor,
itemSelectedBackgroundColor,
itemTextColor,
itemPressedTextColor,
itemSelectedTextColor,
itemFontSize,
itemFontWeight,
itemFontFamily,
itemBorderBottomWidth,
itemBorderBottomColor,
searchInputHeight,
searchInputBackgroundColor,
searchInputBorderRadius,
searchInputBorderWidth,
searchInputBorderColor,
searchInputTextColor,
searchInputFontSize,
searchInputFontFamily,
searchInputPaddingHorizontal,
searchInputPaddingVertical,
disabled = false,
justifyContent,
alignItems,
alignSelf,
flexDirection,
flex,
flexWrap,
flexGrow,
flexShrink,
flexBasis,
position,
top,
bottom,
left,
right,
zIndex,
shadowColor,
shadowOffset,
shadowOpacity,
shadowRadius,
elevation,
opacity,
transform,
containerStyle = {},
textStyle = {},
iconStyle = {},
dropdownStyle = {},
itemStyle = {},
itemTextStyle = {},
searchInputStyle = {},
animationDuration = 200,
animationType = "fade",
accessible,
accessibilityLabel,
accessibilityHint,
accessibilityRole,
testID,
activeOpacity,
delayPressIn,
delayPressOut,
delayLongPress,
onPressIn,
onPressOut,
onLongPress,
hitSlop,
pressRetentionOffset,
modalAnimationType = "none",
modalTransparent = true,
modalVisible,
onModalShow,
onModalHide,
}) => {
const [isOpen, setIsOpen] = useState(false);
const [isPressed, setIsPressed] = useState(false);
const [searchQuery, setSearchQuery] = useState("");
const [dropdownLayout, setDropdownLayout] = useState({
x: 0,
y: 0,
width: 0,
height: 0,
});
const [rotateAnimation] = useState(new Animated.Value(0));
const dropdownRef = useRef(null);
const handleToggle = useCallback(() => {
if (disabled) return;
if (!isOpen) {
dropdownRef.current?.measure((x, y, width, height, pageX, pageY) => {
setDropdownLayout({ x: pageX, y: pageY + height, width, height });
});
}
setIsOpen(!isOpen);
Animated.timing(rotateAnimation, {
toValue: !isOpen ? 1 : 0,
duration: animationDuration,
useNativeDriver: true,
}).start();
}, [disabled, isOpen, animationDuration, rotateAnimation]);
const handleItemSelect = useCallback(
(item) => {
onSelect && onSelect(item);
setIsOpen(false);
setSearchQuery("");
Animated.timing(rotateAnimation, {
toValue: 0,
duration: animationDuration,
useNativeDriver: true,
}).start();
},
[onSelect, animationDuration, rotateAnimation]
);
const handlePressIn = useCallback(
(event) => {
setIsPressed(true);
onPressIn && onPressIn(event);
},
[onPressIn]
);
const handlePressOut = useCallback(
(event) => {
setIsPressed(false);
onPressOut && onPressOut(event);
},
[onPressOut]
);
const filteredData = useCallback(() => {
if (!searchable || !searchQuery.trim()) return data;
return data.filter((item) => {
const label = item[labelKey]?.toString().toLowerCase() || "";
return label.includes(searchQuery.toLowerCase());
});
}, [data, searchQuery, searchable, labelKey]);
const getSelectedItem = useCallback(() => {
return data.find((item) => item[valueKey] === value);
}, [data, value, valueKey]);
const getIconSpacing = useCallback(() => {
if (iconPosition === "left" && iconSpacingLeft !== undefined)
return iconSpacingLeft;
if (iconPosition === "right" && iconSpacingRight !== undefined)
return iconSpacingRight;
if (iconSpacingHorizontal !== undefined) return iconSpacingHorizontal;
if (iconSpacing !== undefined) return iconSpacing;
return WIDTH(10);
}, [
iconPosition,
iconSpacingLeft,
iconSpacingRight,
iconSpacingHorizontal,
iconSpacing,
]);
const buildOuterContainerStyles = useCallback(() => {
const marginStyles = {};
if (margin !== undefined) {
if (typeof margin === "number") {
marginStyles.margin = margin;
} else if (Array.isArray(margin)) {
switch (margin.length) {
case 1:
marginStyles.margin = margin[0];
break;
case 2:
marginStyles.marginVertical = margin[0];
marginStyles.marginHorizontal = margin[1];
break;
case 3:
marginStyles.marginTop = margin[0];
marginStyles.marginHorizontal = margin[1];
marginStyles.marginBottom = margin[2];
break;
case 4:
marginStyles.marginTop = margin[0];
marginStyles.marginLeft = margin[1];
marginStyles.marginBottom = margin[2];
marginStyles.marginRight = margin[3];
break;
}
}
}
if (marginVertical !== undefined)
marginStyles.marginVertical = marginVertical;
if (marginHorizontal !== undefined)
marginStyles.marginHorizontal = marginHorizontal;
if (marginTop !== undefined) marginStyles.marginTop = marginTop;
if (marginBottom !== undefined) marginStyles.marginBottom = marginBottom;
if (marginLeft !== undefined) marginStyles.marginLeft = marginLeft;
if (marginRight !== undefined) marginStyles.marginRight = marginRight;
return marginStyles;
}, [
margin,
marginHorizontal,
marginVertical,
marginTop,
marginBottom,
marginLeft,
marginRight,
]);
const buildInnerContainerStyles = useCallback(() => {
const styles = {
width: width !== undefined ? width : "100%",
height: height !== undefined ? height : HEIGHT(50),
backgroundColor:
backgroundColor !== undefined ? backgroundColor : R.colors.white,
borderRadius: borderRadius !== undefined ? borderRadius : 8,
borderWidth: borderWidth !== undefined ? borderWidth : 1,
borderColor: borderColor !== undefined ? borderColor : R.colors.grey,
paddingHorizontal:
paddingHorizontal !== undefined ? paddingHorizontal : WIDTH(15),
flexDirection: flexDirection !== undefined ? flexDirection : "row",
alignItems: alignItems !== undefined ? alignItems : "center",
justifyContent:
justifyContent !== undefined ? justifyContent : "space-between",
};
if (disabled && disabledBackgroundColor !== undefined)
styles.backgroundColor = disabledBackgroundColor;
else if (isPressed && pressedBackgroundColor !== undefined)
styles.backgroundColor = pressedBackgroundColor;
if (paddingVertical !== undefined) {
styles.paddingTop = paddingVertical;
styles.paddingBottom = paddingVertical;
}
if (paddingTop !== undefined) styles.paddingTop = paddingTop;
if (paddingBottom !== undefined) styles.paddingBottom = paddingBottom;
if (paddingLeft !== undefined) styles.paddingLeft = paddingLeft;
if (paddingRight !== undefined) styles.paddingRight = paddingRight;
if (borderTopLeftRadius !== undefined)
styles.borderTopLeftRadius = borderTopLeftRadius;
if (borderTopRightRadius !== undefined)
styles.borderTopRightRadius = borderTopRightRadius;
if (borderBottomLeftRadius !== undefined)
styles.borderBottomLeftRadius = borderBottomLeftRadius;
if (borderBottomRightRadius !== undefined)
styles.borderBottomRightRadius = borderBottomRightRadius;
if (borderTopWidth !== undefined) styles.borderTopWidth = borderTopWidth;
if (borderBottomWidth !== undefined)
styles.borderBottomWidth = borderBottomWidth;
if (borderLeftWidth !== undefined) styles.borderLeftWidth = borderLeftWidth;
if (borderRightWidth !== undefined)
styles.borderRightWidth = borderRightWidth;
if (borderTopColor !== undefined) styles.borderTopColor = borderTopColor;
if (borderBottomColor !== undefined)
styles.borderBottomColor = borderBottomColor;
if (borderLeftColor !== undefined) styles.borderLeftColor = borderLeftColor;
if (borderRightColor !== undefined)
styles.borderRightColor = borderRightColor;
if (borderStyle !== undefined) styles.borderStyle = borderStyle;
if (alignSelf !== undefined) styles.alignSelf = alignSelf;
if (flex !== undefined) styles.flex = flex;
if (flexWrap !== undefined) styles.flexWrap = flexWrap;
if (flexGrow !== undefined) styles.flexGrow = flexGrow;
if (flexShrink !== undefined) styles.flexShrink = flexShrink;
if (flexBasis !== undefined) styles.flexBasis = flexBasis;
if (position !== undefined) styles.position = position;
if (top !== undefined) styles.top = top;
if (bottom !== undefined) styles.bottom = bottom;
if (left !== undefined) styles.left = left;
if (right !== undefined) styles.right = right;
if (zIndex !== undefined) styles.zIndex = zIndex;
if (shadowColor !== undefined) styles.shadowColor = shadowColor;
if (shadowOffset !== undefined) styles.shadowOffset = shadowOffset;
if (shadowOpacity !== undefined) styles.shadowOpacity = shadowOpacity;
if (shadowRadius !== undefined) styles.shadowRadius = shadowRadius;
if (elevation !== undefined) styles.elevation = elevation;
if (opacity !== undefined) styles.opacity = opacity;
if (transform !== undefined) styles.transform = transform;
return styles;
}, [
width,
height,
backgroundColor,
pressedBackgroundColor,
disabledBackgroundColor,
borderRadius,
borderWidth,
borderColor,
paddingHorizontal,
flexDirection,
alignItems,
justifyContent,
disabled,
isPressed,
paddingVertical,
paddingTop,
paddingBottom,
paddingLeft,
paddingRight,
borderTopLeftRadius,
borderTopRightRadius,
borderBottomLeftRadius,
borderBottomRightRadius,
borderTopWidth,
borderBottomWidth,
borderLeftWidth,
borderRightWidth,
borderTopColor,
borderBottomColor,
borderLeftColor,
borderRightColor,
borderStyle,
alignSelf,
flex,
flexWrap,
flexGrow,
flexShrink,
flexBasis,
position,
top,
bottom,
left,
right,
zIndex,
shadowColor,
shadowOffset,
shadowOpacity,
shadowRadius,
elevation,
opacity,
transform,
]);
const buildTextStyles = useCallback(() => {
const selectedItem = getSelectedItem();
const isPlaceholder = !selectedItem;
const styles = {
flex: 1,
fontSize: fontSize !== undefined ? fontSize : getFontSize(14),
color: textColor !== undefined ? textColor : R.colors.black,
fontFamily: fontFamily !== undefined ? fontFamily : R.fonts.InterRegular,
};
if (isPlaceholder) {
if (placeholderColor !== undefined) styles.color = placeholderColor;
if (placeholderFontSize !== undefined)
styles.fontSize = placeholderFontSize;
if (placeholderFontWeight !== undefined)
styles.fontWeight = placeholderFontWeight;
if (placeholderFontFamily !== undefined)
styles.fontFamily = placeholderFontFamily;
} else {
if (disabled && disabledTextColor !== undefined)
styles.color = disabledTextColor;
else if (isPressed && pressedTextColor !== undefined)
styles.color = pressedTextColor;
}
if (fontWeight !== undefined && !isPlaceholder)
styles.fontWeight = fontWeight;
if (fontStyle !== undefined) styles.fontStyle = fontStyle;
if (textAlign !== undefined) styles.textAlign = textAlign;
if (lineHeight !== undefined) styles.lineHeight = lineHeight;
if (letterSpacing !== undefined) styles.letterSpacing = letterSpacing;
return styles;
}, [
fontSize,
textColor,
fontFamily,
placeholderColor,
placeholderFontSize,
placeholderFontWeight,
placeholderFontFamily,
disabled,
isPressed,
disabledTextColor,
pressedTextColor,
fontWeight,
fontStyle,
textAlign,
lineHeight,
letterSpacing,
getSelectedItem,
]);
const buildDropdownStyles = useCallback(() => {
const screenHeight = Dimensions.get("window").height;
const maxHeight =
dropdownMaxHeight !== undefined ? dropdownMaxHeight : HEIGHT(200);
const styles = {
position: "absolute",
left: dropdownLayout.x,
top: dropdownLayout.y,
width: dropdownLayout.width,
maxHeight: maxHeight,
backgroundColor:
dropdownBackgroundColor !== undefined
? dropdownBackgroundColor
: R.colors.white,
borderRadius:
dropdownBorderRadius !== undefined ? dropdownBorderRadius : 8,
borderWidth: dropdownBorderWidth !== undefined ? dropdownBorderWidth : 1,
borderColor:
dropdownBorderColor !== undefined ? dropdownBorderColor : R.colors.grey,
shadowColor:
dropdownShadowColor !== undefined ? dropdownShadowColor : "#000",
shadowOffset:
dropdownShadowOffset !== undefined
? dropdownShadowOffset
: { width: 0, height: 2 },
shadowOpacity:
dropdownShadowOpacity !== undefined ? dropdownShadowOpacity : 0.25,
shadowRadius:
dropdownShadowRadius !== undefined ? dropdownShadowRadius : 3.84,
elevation: dropdownElevation !== undefined ? dropdownElevation : 5,
zIndex: 1000,
};
if (dropdownLayout.y + maxHeight > screenHeight - HEIGHT(50)) {
styles.top = dropdownLayout.y - dropdownLayout.height - maxHeight;
}
return styles;
}, [
dropdownLayout,
dropdownMaxHeight,
dropdownBackgroundColor,
dropdownBorderRadius,
dropdownBorderWidth,
dropdownBorderColor,
dropdownShadowColor,
dropdownShadowOffset,
dropdownShadowOpacity,
dropdownShadowRadius,
dropdownElevation,
]);
const buildItemStyles = useCallback(
(item, isSelected, isItemPressed) => {
const styles = {
height: itemHeight !== undefined ? itemHeight : HEIGHT(45),
paddingHorizontal:
itemPaddingHorizontal !== undefined
? itemPaddingHorizontal
: WIDTH(15),
paddingVertical:
itemPaddingVertical !== undefined ? itemPaddingVertical : HEIGHT(12),
justifyContent: "center",
borderBottomWidth:
itemBorderBottomWidth !== undefined ? itemBorderBottomWidth : 0.5,
borderBottomColor:
itemBorderBottomColor !== undefined
? itemBorderBottomColor
: R.colors.lightGrey,
};
if (isSelected && itemSelectedBackgroundColor !== undefined)
styles.backgroundColor = itemSelectedBackgroundColor;
else if (isItemPressed && itemPressedBackgroundColor !== undefined)
styles.backgroundColor = itemPressedBackgroundColor;
else if (itemBackgroundColor !== undefined)
styles.backgroundColor = itemBackgroundColor;
return styles;
},
[
itemHeight,
itemPaddingHorizontal,
itemPaddingVertical,
itemBorderBottomWidth,
itemBorderBottomColor,
itemSelectedBackgroundColor,
itemPressedBackgroundColor,
itemBackgroundColor,
]
);
const buildItemTextStyles = useCallback(
(item, isSelected, isItemPressed) => {
const styles = {
fontSize: itemFontSize !== undefined ? itemFontSize : getFontSize(14),
fontFamily:
itemFontFamily !== undefined ? itemFontFamily : R.fonts.InterRegular,
color: itemTextColor !== undefined ? itemTextColor : R.colors.black,
};
if (isSelected && itemSelectedTextColor !== undefined)
styles.color = itemSelectedTextColor;
else if (isItemPressed && itemPressedTextColor !== undefined)
styles.color = itemPressedTextColor;
if (itemFontWeight !== undefined) styles.fontWeight = itemFontWeight;
return styles;
},
[
itemFontSize,
itemFontFamily,
itemTextColor,
itemSelectedTextColor,
itemPressedTextColor,
itemFontWeight,
]
);
const getCurrentIconColor = useCallback(() => {
if (disabled && disabledIconColor !== undefined) return disabledIconColor;
if (isPressed && pressedIconColor !== undefined) return pressedIconColor;
if (iconColor !== undefined) return iconColor;
return R.colors.grey;
}, [disabled, isPressed, disabledIconColor, pressedIconColor, iconColor]);
const renderIcon = useCallback(() => {
const spacing = getIconSpacing();
const currentIconColor = getCurrentIconColor();
const DefaultIcon =
iconComponent ||
(() => (
<View
style={{
width: iconSize !== undefined ? iconSize : 12,
height: iconSize !== undefined ? iconSize : 12,
borderRightWidth: 1,
borderBottomWidth: 1,
borderColor: currentIconColor,
transform: [{ rotate: "45deg" }],
}}
/>
));
const iconContainerStyles = {
marginLeft: iconPosition === "right" ? spacing : 0,
marginRight: iconPosition === "left" ? spacing : 0,
transform: [
{
rotate: rotateAnimation.interpolate({
inputRange: [0, 1],
outputRange: ["0deg", "90deg"],
}),
},
],
};
return (
<Animated.View style={iconContainerStyles}>
{typeof DefaultIcon === "function" ? (
<DefaultIcon
size={iconSize}
color={currentIconColor}
style={iconStyle}
/>
) : (
React.cloneElement(DefaultIcon, {
size: iconSize,
color: currentIconColor,
style: [DefaultIcon.props?.style, iconStyle],
})
)}
</Animated.View>
);
}, [
iconComponent,
iconSize,
iconPosition,
iconStyle,
getIconSpacing,
getCurrentIconColor,
rotateAnimation,
]);
const renderSelectedText = useCallback(() => {
const selectedItem = getSelectedItem();
if (renderSelectedItem && selectedItem) {
return renderSelectedItem(selectedItem);
}
const displayText = selectedItem ? selectedItem[labelKey] : placeholder;
return (
<Text
style={[buildTextStyles(), textStyle]}
numberOfLines={numberOfLines !== undefined ? numberOfLines : 1}
>
{displayText}
</Text>
);
}, [
getSelectedItem,
renderSelectedItem,
labelKey,
placeholder,
buildTextStyles,
textStyle,
numberOfLines,
]);
const renderSearchInput = useCallback(() => {
if (!searchable) return null;
const searchStyles = {
height: searchInputHeight !== undefined ? searchInputHeight : HEIGHT(40),
backgroundColor:
searchInputBackgroundColor !== undefined
? searchInputBackgroundColor
: R.colors.lightGrey,
borderRadius:
searchInputBorderRadius !== undefined ? searchInputBorderRadius : 6,
borderWidth:
searchInputBorderWidth !== undefined ? searchInputBorderWidth : 1,
borderColor:
searchInputBorderColor !== undefined
? searchInputBorderColor
: R.colors.grey,
color:
searchInputTextColor !== undefined
? searchInputTextColor
: R.colors.black,
fontSize:
searchInputFontSize !== undefined
? searchInputFontSize
: getFontSize(14),
fontFamily:
searchInputFontFamily !== undefined
? searchInputFontFamily
: R.fonts.InterRegular,
paddingHorizontal:
searchInputPaddingHorizontal !== undefined
? searchInputPaddingHorizontal
: WIDTH(12),
paddingVertical:
searchInputPaddingVertical !== undefined
? searchInputPaddingVertical
: HEIGHT(8),
margin: WIDTH(10),
};
return (
<TextInput
style={[searchStyles, searchInputStyle]}
placeholder={searchPlaceholder}
value={searchQuery}
onChangeText={setSearchQuery}
autoFocus={true}
/>
);
}, [
searchable,
searchInputHeight,
searchInputBackgroundColor,
searchInputBorderRadius,
searchInputBorderWidth,
searchInputBorderColor,
searchInputTextColor,
searchInputFontSize,
searchInputFontFamily,
searchInputPaddingHorizontal,
searchInputPaddingVertical,
searchInputStyle,
searchPlaceholder,
searchQuery,
]);
const renderDropdownItem = useCallback(
({ item, index }) => {
const isSelected = item[valueKey] === value;
if (renderItem) {
return renderItem(item, index, isSelected);
}
return (
<DropdownItem
item={item}
isSelected={isSelected}
onSelect={handleItemSelect}
buildItemStyles={buildItemStyles}
buildItemTextStyles={buildItemTextStyles}
itemStyle={itemStyle}
itemTextStyle={itemTextStyle}
labelKey={labelKey}
/>
);
},
[
value,
valueKey,
renderItem,
handleItemSelect,
buildItemStyles,
buildItemTextStyles,
itemStyle,
itemTextStyle,
labelKey,
]
);
const renderDropdownContent = useCallback(() => {
const filtered = filteredData();
return (
<View style={[buildDropdownStyles(), dropdownStyle]}>
{renderSearchInput()}
<FlatList
data={filtered}
renderItem={renderDropdownItem}
keyExtractor={
keyExtractor ||
((item, index) => item[valueKey]?.toString() || index.toString())
}
showsVerticalScrollIndicator={false}
nestedScrollEnabled={true}
/>
</View>
);
}, [
filteredData,
buildDropdownStyles,
dropdownStyle,
renderSearchInput,
renderDropdownItem,
keyExtractor,
valueKey,
]);
const buildTitleStyles = useCallback(() => {
const styles = {
// Default styles
marginBottom: titleMarginBottom !== undefined ? titleMarginBottom : 3,
fontSize: titleFontSize !== undefined ? titleFontSize : R.fontsize.fontsSize12,
color: titleColor !== undefined ? titleColor : R.colors.black,
fontFamily:
titleFontFamily !== undefined ? titleFontFamily : R.fonts.InterRegular,
};
// PERFORMANCE: Apply conditional styles efficiently
if (titleFontWeight !== undefined) styles.fontWeight = titleFontWeight;
if (titleFontStyle !== undefined) styles.fontStyle = titleFontStyle;
if (titleTextAlign !== undefined) styles.textAlign = titleTextAlign;
if (titleLineHeight !== undefined) styles.lineHeight = titleLineHeight;
if (titleLetterSpacing !== undefined)
styles.letterSpacing = titleLetterSpacing;
if (titleTextTransform !== undefined)
styles.textTransform = titleTextTransform;
if (titleTextDecorationLine !== undefined)
styles.textDecorationLine = titleTextDecorationLine;
if (titleTextDecorationColor !== undefined)
styles.textDecorationColor = titleTextDecorationColor;
if (titleTextDecorationStyle !== undefined)
styles.textDecorationStyle = titleTextDecorationStyle;
// FUNCTIONALITY: Margin styles
if (titleMarginTop !== undefined) styles.marginTop = titleMarginTop;
if (titleMarginLeft !== undefined) styles.marginLeft = titleMarginLeft;
if (titleMarginRight !== undefined) styles.marginRight = titleMarginRight;
if (titleMarginHorizontal !== undefined) {
styles.marginLeft = titleMarginHorizontal;
styles.marginRight = titleMarginHorizontal;
}
if (titleMarginVertical !== undefined) {
styles.marginTop = titleMarginVertical;
styles.marginBottom = titleMarginVertical;
}
// FUNCTIONALITY: Padding styles
if (titlePaddingTop !== undefined) styles.paddingTop = titlePaddingTop;
if (titlePaddingBottom !== undefined)
styles.paddingBottom = titlePaddingBottom;
if (titlePaddingLeft !== undefined) styles.paddingLeft = titlePaddingLeft;
if (titlePaddingRight !== undefined)
styles.paddingRight = titlePaddingRight;
if (titlePaddingHorizontal !== undefined) {
styles.paddingLeft = titlePaddingHorizontal;
styles.paddingRight = titlePaddingHorizontal;
}
if (titlePaddingVertical !== undefined) {
styles.paddingTop = titlePaddingVertical;
styles.paddingBottom = titlePaddingVertical;
}
return styles;
}, [
titleMarginBottom,
titleFontSize,
titleColor,
titleFontFamily,
titleFontWeight,
titleFontStyle,
titleTextAlign,
titleLineHeight,
titleLetterSpacing,
titleTextTransform,
titleTextDecorationLine,
titleTextDecorationColor,
titleTextDecorationStyle,
titleMarginTop,
titleMarginLeft,
titleMarginRight,
titleMarginHorizontal,
titleMarginVertical,
titlePaddingTop,
titlePaddingBottom,
titlePaddingLeft,
titlePaddingRight,
titlePaddingHorizontal,
titlePaddingVertical,
]);
// FUNCTIONALITY: Build required symbol styles
const buildRequiredSymbolStyles = useCallback(() => {
const styles = {
color: requiredSymbolColor !== undefined ? requiredSymbolColor : "red",
};
if (requiredSymbolFontSize !== undefined)
styles.fontSize = requiredSymbolFontSize;
if (requiredSymbolFontWeight !== undefined)
styles.fontWeight = requiredSymbolFontWeight;
if (requiredSymbolFontFamily !== undefined)
styles.fontFamily = requiredSymbolFontFamily;
return styles;
}, [
requiredSymbolColor,
requiredSymbolFontSize,
requiredSymbolFontWeight,
requiredSymbolFontFamily,
]);
// UI/UX: Enhanced title rendering with better required symbol handling
const renderTitle = useCallback(() => {
if (!title) return null;
// FUNCTIONALITY: Check for required marker (supports * hoặc custom marker)
const hasRequiredMarker = title.includes("*") || title.includes("required");
if (hasRequiredMarker) {
// PERFORMANCE: Efficient string processing
const cleanTitle = title.replace(/\*|required/g, "").trim();
const requiredText =
requiredSymbolText !== undefined ? requiredSymbolText : " *";
return (
<Text style={[buildTitleStyles(), titleStyle]}>
{cleanTitle}
<Text style={[buildRequiredSymbolStyles(), titleRequiredStyle]}>
{requiredText}
</Text>
</Text>
);
}
return <Text style={[buildTitleStyles(), titleStyle]}>{title}</Text>;
}, [
title,
buildTitleStyles,
titleStyle,
buildRequiredSymbolStyles,
titleRequiredStyle,
requiredSymbolText,
]);
const outerContainerStyles = buildOuterContainerStyles();
const innerContainerStyles = [buildInnerContainerStyles(), containerStyle];
return (
<>
<View style={outerContainerStyles}>
{renderTitle()}
<TouchableOpacity
ref={dropdownRef}
style={innerContainerStyles}
onPress={handleToggle}
onPressIn={handlePressIn}
onPressOut={handlePressOut}
onLongPress={onLongPress}
disabled={disabled}
activeOpacity={activeOpacity}
delayPressIn={delayPressIn}
delayPressOut={delayPressOut}
delayLongPress={delayLongPress}
hitSlop={hitSlop}
pressRetentionOffset={pressRetentionOffset}
accessible={accessible}
accessibilityLabel={accessibilityLabel}
accessibilityHint={accessibilityHint}
accessibilityRole={accessibilityRole}
testID={testID}
>
{iconPosition === "left" && renderIcon()}
{renderSelectedText()}
{iconPosition === "right" && renderIcon()}
</TouchableOpacity>
</View>
<Modal
visible={modalVisible !== undefined ? modalVisible : isOpen}
transparent={modalTransparent}
animationType={modalAnimationType}
onShow={onModalShow}
onRequestClose={() => {
setIsOpen(false);
Animated.timing(rotateAnimation, {
toValue: 0,
duration: animationDuration,
useNativeDriver: true,
}).start();
onModalHide && onModalHide();
}}
>
<TouchableOpacity
style={styles.backdrop}
activeOpacity={1}
onPress={() => {
setIsOpen(false);
Animated.timing(rotateAnimation, {
toValue: 0,
duration: animationDuration,
useNativeDriver: true,
}).start();
}}
>
{renderDropdownContent()}
</TouchableOpacity>
</Modal>
</>
);
};
export default DropdownSelect;
const styles = StyleSheet.create({
backdrop: {
flex: 1,
backgroundColor: "transparent",
},
requiredSymbol: {
color: "red",
},
});
import React, {useState} from 'react'; import React from 'react';
import { import {
Image,
Platform,
SafeAreaView,
StatusBar,
StyleSheet, StyleSheet,
Text, Text,
TouchableOpacity, TouchableOpacity,
...@@ -11,30 +7,34 @@ import { ...@@ -11,30 +7,34 @@ import {
} from 'react-native'; } from 'react-native';
import R from '../../assets/R'; import R from '../../assets/R';
import { import {
getWidth,
HEIGHTXD,
WIDTHXD,
WIDTHXDICON,
HEIGHT, HEIGHT,
getFontSize, getFontSize,
WIDTH, WIDTH,
} from '../../config/Functions'; } from '../../config/Functions';
import Icon from 'react-native-vector-icons/AntDesign';
import {useNavigation} from '@react-navigation/native'; import {useNavigation} from '@react-navigation/native';
import SnackBar from '../SnackBar';
const Header = props => { const Header = props => {
const {title, isBack} = props; const {title, isBack, isSearch} = props;
const navigate = useNavigation(); const navigate = useNavigation();
const IconBack = R.images.icBack;
const IconSearch = R.images.icSearch;
return ( return (
<View style={styles.headerContainer}> <View style={styles.headerContainer}>
{isSearch && (
<TouchableOpacity
style={styles.btnSearch}
onPress={() => {}}>
<IconSearch stroke={R.colors.white} strokeWidth={2} width={20} height={20} />
</TouchableOpacity>
)}
<Text numberOfLines={1} style={styles.txtTitle}> <Text numberOfLines={1} style={styles.txtTitle}>
{title} {title}
</Text> </Text>
{isBack && ( {isBack && (
<TouchableOpacity <TouchableOpacity
style={styles.btnBack} style={styles.btnBack}
onPress={() => navigate.goBack()}> onPress={() => navigate.goBack()}>
<Icon color={R.colors.black} name={'arrowleft'} size={22} /> <IconBack width={20} height={20} />
</TouchableOpacity> </TouchableOpacity>
)} )}
</View> </View>
...@@ -45,13 +45,13 @@ export default Header; ...@@ -45,13 +45,13 @@ export default Header;
const styles = StyleSheet.create({ const styles = StyleSheet.create({
headerContainer: { headerContainer: {
height: HEIGHT(45), height: HEIGHT(50),
width: '100%', width: '100%',
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
marginBottom: 2, marginBottom: 2,
justifyContent: 'center', justifyContent: 'center',
backgroundColor: R.colors.white, backgroundColor: R.colors.blue500,
shadowColor: '#000', shadowColor: '#000',
shadowOffset: { shadowOffset: {
width: 0, width: 0,
...@@ -67,12 +67,20 @@ const styles = StyleSheet.create({ ...@@ -67,12 +67,20 @@ const styles = StyleSheet.create({
fontSize: getFontSize(16), fontSize: getFontSize(16),
textAlign: 'center', textAlign: 'center',
fontWeight: 'bold', fontWeight: 'bold',
color: R.colors.black, color: R.colors.white,
}, },
btnBack: { btnBack: {
position: 'absolute', position: 'absolute',
left: WIDTH(10), left: 5,
width: WIDTH(35), width: WIDTH(45),
height: HEIGHT(30),
alignItems: 'center',
justifyContent: 'center',
},
btnSearch: {
position: 'absolute',
right: 5,
width: WIDTH(45),
height: HEIGHT(30), height: HEIGHT(30),
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
......
import React from 'react';
import {View, Text, TextInput} from 'react-native';
import {WIDTHXD, getFontXD, WIDTH, HEIGHT} from '../../config/Functions';
import R from '../../assets/R';
import {RFValue} from 'react-native-responsive-fontsize';
const TextField = props => {
const {
title,
onChangeText,
isPassword,
maxLength,
isNumber,
value,
editable,
error,
} = props;
const renderMess = () => {
if (value && value.trim().length > 0) return `${title} không hợp lệ`;
return `Vui lòng nhập ${title ? title.toLowerCase() : messageError} `;
};
return (
<View style={{marginVertical: 5, marginHorizontal: WIDTH(15)}}>
<Text
style={{
fontSize: 16,
color: R.colors.color777,
marginBottom: 5,
}}>
{title ? title : ''}
</Text>
<TextInput
maxLength={maxLength ? maxLength : 256}
placeholderTextColor={R.colors.placeHolder}
editable={editable != null ? editable : true}
secureTextEntry={isPassword}
autoCapitalize="none"
value={value}
keyboardType={isNumber ? 'number-pad' : 'default'}
onChangeText={val => onChangeText(val)}
style={{
height: HEIGHT(35),
color: 'black',
borderRadius: HEIGHT(6),
fontSize: 16,
paddingVertical: 5,
paddingHorizontal: 10,
backgroundColor: '#EFF3F5',
}}
/>
<View
style={{
height: HEIGHT(20),
marginTop: 5,
}}>
{error && !error.ref?.value && (
<Text
ellipsizeMode={'tail'}
numberOfLines={1}
style={{
flex: 1,
color: R.colors.red,
fontSize: RFValue(10),
}}>
{error.message ? error.message : renderMess()}
</Text>
)}
</View>
</View>
);
};
export default React.memo(TextField);
import React, { useRef } from 'react';
import {
View,
Text,
TextInput,
StyleSheet,
Platform,
} from 'react-native';
import R from '../../assets/R';
const CustomTextInput = (props) => {
const {
title,
placeholder,
required,
value,
onChangeText,
backgroundColor, // FUNCTIONALITY: Prop backgroundColor tùy chỉnh
...restProps
} = props;
const inputRef = useRef(null);
return (
<View style={styles.container}>
{/* FUNCTIONALITY: Title với sao đỏ nếu required */}
{title && (
<View style={styles.titleContainer}>
<Text style={styles.title}>
{title}
{required && <Text style={styles.required}> *</Text>}
</Text>
</View>
)}
{/* FUNCTIONALITY: Input container với background tùy chỉnh */}
<View style={[
styles.inputContainer,
backgroundColor && { backgroundColor } // NOTE: Chỉ apply backgroundColor khi có truyền vào
]}>
<TextInput
ref={inputRef}
style={styles.input}
placeholder={placeholder}
placeholderTextColor={R.colors.grey_100}
value={value}
onChangeText={onChangeText}
{...restProps}
/>
</View>
</View>
);
};
const styles = StyleSheet.create({
// FUNCTIONALITY: Container wrapper
container: {
width: '100%',
marginVertical: 5
},
// UI/UX: Container cho title
titleContainer: {
flexDirection: 'row',
marginBottom: 3,
},
// UI/UX: Title text
title: {
fontSize: R.fontsize.fontsSize12,
fontFamily: R.fonts.InterRegular,
fontWeight: '400',
color: R.colors.black
},
// UI/UX: Dấu sao đỏ bắt buộc
required: {
color: R.colors.red,
fontSize: R.fontsize.fontsSize12,
fontFamily: R.fonts.InterRegular,
fontWeight: '500',
},
// UI/UX: Input container - KHÔNG có backgroundColor mặc định
inputContainer: {
// NOTE: Không set backgroundColor ở đây
borderRadius: 10,
borderWidth: 1,
borderColor: R.colors.grey_200,
paddingHorizontal: 15,
},
// UI/UX: Input text styles
input: {
fontSize: R.fontsize.fontsSize10,
fontFamily: R.fonts.InterRegular,
fontWeight: '400',
color: R.colors.black, // FIXME: Đổi từ grey_200 sang black để text rõ hơn
padding: 0,
minHeight: 40,
},
});
export default CustomTextInput;
...@@ -15,7 +15,7 @@ import i18n from '../../helper/i18/i18n'; ...@@ -15,7 +15,7 @@ import i18n from '../../helper/i18/i18n';
import {connect} from 'react-redux'; import {connect} from 'react-redux';
import {hideLoading, showLoading} from '../../actions/loadingAction'; import {hideLoading, showLoading} from '../../actions/loadingAction';
import {uploadFile} from '../../apis/Functions/config'; import {uploadFile} from '../../apis/Functions/config';
import {showAlert, TYPE} from '../DropdownAlert'; import {showAlert, TYPE} from '../Dropdown/DropdownAlert';
import {saveUserToRedux} from '../../actions/users'; import {saveUserToRedux} from '../../actions/users';
import {ASYNC_STORE_KEY} from '../../config/constants'; import {ASYNC_STORE_KEY} from '../../config/constants';
import AsyncStorage from '@react-native-community/async-storage'; import AsyncStorage from '@react-native-community/async-storage';
......
export default {
EnterPhoneNumberToLogin: 'Enter phone number to login',
Enter: 'Enter',
PhoneNumber: 'Phone number',
VerifyCodeWillSendPhoneNumber:
'The verification code will be sent to the phone number\n you just entered',
Login: 'Login',
ForgotPassword: 'Forgot password?',
TermsOfUse: 'Terms of use',
PrivacyPolicy: 'Privacy policy',
Password: 'Password',
ChangeOtherPhoneNumber: 'Change other phone number',
VerifyCodeWillSend: 'A real 4-digit code will be sent to the phone number ',
ResendVerifyCode: 'Resend verify code',
Continue: 'Next',
FirstLastName: 'First and last name',
CreateCreditCardSuccess: 'Create card credit success',
UpdateCreditCardSuccess: 'Update card credit success',
ReferralCode: 'Referral code (if any)',
And: 'and',
PleaseEnterField: 'Please enter this field',
PleaseSelect: 'Please select ',
PleaseEnter: 'Please enter ',
Go: 'Go',
Upgrade: 'Upgrade',
InvestPackage: 'Invest Package',
AccountBalance: 'Account balance',
Birthday: 'Birthday',
Sex: 'Sex',
Male: 'Male',
Female: 'Female',
IdentifyInfo: 'Identify Information',
IdentifyID: 'Identify ID',
DateOfIdentifyId: 'Date of identify ID',
PlaceOfIdentifyId: 'Place of identify ID',
Hometown: 'Hometown',
Home: 'Home',
Notification: 'Notification',
Account: 'Account',
PersonalInfo: 'Personal Infor',
AboutUs: 'About us',
AccountSupport: 'Support',
Setting: 'Setting',
Logout: 'Logout',
Email: 'Email',
ProvinceCity: 'Province/City',
ContactAddress: 'Contact address',
Update: 'Update',
OnlineSupport: 'Online support', export default {
SupportByOperator: 'Support by operator', Search :'Search',
FanpageFacebook: 'Fanpage Facebook', Home:'Home',
Terms2: 'Terms', Elearning:'Elearning',
Version: 'Version', QrCode:'QrCode',
SupportPhoneNumber: 'Support phone number', Notification:'Notification',
WorkTime: 'Work time', Profile:'Profile',
Address: 'Address', RollCall:'Roll Call',
ChangePassword: 'Change password', Debt:'Debt',
CommonSetting: 'Common setting', CertificateRegisitor:'Certificate Regisitor',
OldPassword: 'Old password', filterByDay:'Filter by day',
NewPassword: 'New password', filterBy3Day:'Filter by 3 day',
ConfirmNewPassword: 'Confirm new password', filterByWeek:'Filter by week',
ReceiveNotification: 'Receive notification', filterByMonth:'Filter by month',
Language: 'Language', homeSchedule:'Home schedule',
English: 'English', TrainingPoint:'Training Point',
Vietnamese: 'Vietnamese', ExamSchedule:'Exam Schedule'
LogoutConfirm: 'Do you really want to sign out?',
No: 'No',
ResendVerifyCode: 'Resend verify code',
VerifyCode: 'Verification code',
GetVerifyCode: 'Get verification code',
Confirm: 'Confirm',
NullDataSearch: 'No data',
No_Internet: 'No internet',
Select_source_image: 'Select the source of the image',
Photo_library: 'Photo library',
Take_photo: 'Take a photo',
}; };
export default { export default {
EnterPhoneNumberToLogin: 'Nhập số điện thoại để đăng nhập', Search:'Tìm kiếm',
Enter: 'Nhập', Home:'Trang chủ',
PhoneNumber: 'Số điện thoại', Elearning:'Học trực tuyến',
VerifyCodeWillSendPhoneNumber: QrCode:'Máy quét',
'Mã xác thực sẽ gửi về số điện thoại\n mà bạn vừa nhập', Notification:'Thông báo',
Login: 'Đăng nhập', Profile:'Cá nhân',
RollCall:'Điểm danh',
LoginFalse: 'Đăng nhập thấi bại', Debt:'Công nợ',
ForgotPassword: 'Quên mật khẩu?', CertificateRegisitor:'Đăng kí chứng chỉ',
filterByDay:'Lọc theo ngày',
TermsOfUse: 'Điều khoản sử dụng', filterBy3Day:'Lọc 3 ngày',
PrivacyPolicy: ' Chính sách bảo mật', filterByWeek:'Lọc theo tuần',
Password: 'Mật khẩu', filterByMonth:'Lọc theo tháng',
ChangeOtherPhoneNumber: 'Đổi số điện thoại khác', homeSchedule:'Trang chủ lịch học',
VerifyCodeWillSend: 'Mã các thực gồm 6 chữ số sẽ được gửi đến số điện thoại ', TrainingPoint:'Điểm rèn luyện',
ResendVerifyCode: 'Gửi lại mã xác nhận', ExamSchedule:'Lịch thi'
Continue: 'Tiếp tục',
FirstLastName: 'Họ và tên',
ReferralCode: 'Mã giới thiệu (nếu có)',
And: 'và',
PleaseEnterField: 'Vui lòng nhập trường này',
PleaseSelect: 'Vui lòng chọn ',
PleaseEnter: 'Vui lòng nhập ',
Go: 'Đi vào',
Upgrade: 'Nâng cấp',
Home: 'Trang chủ',
Notification: 'Thông báo',
Account: 'Tài khoản',
PersonalInfo: 'Thông tin cá nhân',
AboutUs: 'Về chúng tôi',
AccountSupport: 'Hỗ trợ tài khoản',
Setting: 'Cài đặt',
Logout: 'Đăng xuất',
No: 'Không',
Birthday: 'Ngày sinh',
Sex: 'Giới tính',
Male: 'Nam',
Female: 'Nữ',
IdentifyInfo: 'Thông tin định danh',
IdentifyID: 'Số CMND',
DateOfIdentifyId: 'Ngày cấp',
PlaceOfIdentifyId: 'Nơi cấp',
Hometown: 'Quê quán',
Update: 'Cập nhật',
Ok: 'Đồng ý',
Cancel: 'Hủy',
ProvinceCity: 'Tỉnh/Thành phố',
ContactAddress: 'Địa chỉ liên hệ',
OnlineSupport: 'Hỗ trợ trực tuyến',
SupportByOperator: 'Hỗ trợ qua tổng đài viên',
FanpageFacebook: 'Fanpage Facebook',
Terms2: 'Điều khoản sử dụng',
Version: 'Phiên bản',
SupportPhoneNumber: 'Số điện thoại viên',
WorkTime: 'Thời gian làm việc',
Address: 'Địa chỉ',
NullDataSearch: 'Không có dữ liệu',
ChangePassword: 'Đổi mật khẩu',
CommonSetting: 'Thiết lập chung',
OldPassword: 'Mật khẩu cũ',
NewPassword: 'Mật khẩu mới',
ConfirmNewPassword: 'Xác nhận mật khẩu mới',
ReceiveNotification: 'Nhận thông báo',
Language: 'Ngôn ngữ',
English: 'Tiếng Anh',
Vietnamese: 'Tiếng Việt',
LogoutConfirm: 'Bạn thật sự muốn đăng xuất không?',
GetVerifyCode: 'Nhận mã xác thực',
VerifyCode: 'Mã xác thực',
Confirm: 'Xác nhận',
No_Internet: 'Không có kết nốt Internet',
Check_Internet_Connect: 'Kiểm tra lại đường truyền',
Retry: 'Thử lại',
Select_source_image: 'Chọn nguồn lấy ảnh',
Photo_library: 'Thư viện ảnh',
Take_photo: 'Chụp ảnh',
}; };
import React, { useEffect, useState } from "react";
import {
DeviceEventEmitter,
Image,
StyleSheet,
Text,
TouchableOpacity,
View,
} from "react-native";
import { createDrawerNavigator } from "@react-navigation/drawer";
import { connect } from "react-redux";
import ClassSchedule from "../../screens/class_schedule";
import FilterDateView from '../../screens/class_schedule/filter_date';
import * as ScreenName from "../ScreenNames";
import CustomDrawerContent from "./itemDrawer";
import R from "../../assets/R";
import FilterWeekView from "../../screens/class_schedule/filter_week/view";
import Filter3Date from "../../screens/class_schedule/filter_3_date";
const Drawer = createDrawerNavigator();
const ArrowLeft = R.images.icBack;
const IconMenu = R.images.icMenu;
const IconSearch = R.images.icSearch;
const DrawerNavigatorView = (props) => {
const [reload, setReload] = useState(false);
const [currentDate, setCurrentDate] = useState(new Date());
const HeaderBackButton = ({ onPress, canGoBack }) => (
<TouchableOpacity
style={styles.headerBackButton}
onPress={onPress}
activeOpacity={0.7}
>
<ArrowLeft fill={"white"} />
</TouchableOpacity>
);
useEffect(() => {
const setLanguageListener = DeviceEventEmitter.addListener(
"setLanguage",
(value) => {
setReload((prev) => !prev);
}
);
const dateChangeListener = DeviceEventEmitter.addListener(
"onDateChange",
(date) => {
setCurrentDate(new Date(date));
}
);
return () => {
setLanguageListener.remove();
dateChangeListener.remove();
};
}, []);
const MenuButton = ({ onPress }) => (
<TouchableOpacity style={styles.menuButton} onPress={onPress}>
<IconMenu width={24} height={24} stroke={R.colors.white}/>
</TouchableOpacity>
);
const HeaderTitle = ({ navigation, route }) => {
const getHeaderTitle = () => {
if (route.name === ScreenName.FILTERDATE) {
return `Tháng ${currentDate.getMonth() + 1}`;
}
if (route.name === ScreenName.FILTER3DATE) {
return `Tháng ${currentDate.getMonth() + 1}`;
}
if (route.name === ScreenName.FILTERWEEK) {
return `Tháng ${currentDate.getMonth() + 1}`;
}
return '';
};
return (
<TouchableOpacity style={styles.headerTitleButton}>
<Text style={styles.headerTitleText}>
{getHeaderTitle()}
</Text>
</TouchableOpacity>
);
};
return (
<Drawer.Navigator
drawerContent={(drawerProps) => <CustomDrawerContent {...drawerProps} />}
initialRouteName={ScreenName.CLASSSCHEDULE}
screenOptions={({ navigation, route }) => ({
headerLeft: () => (
<View style={styles.headerLeftContainer}>
<HeaderBackButton
onPress={() => {
if (navigation.canGoBack()) {
navigation.pop();
} else {
navigation.toggleDrawer();
}
}}
canGoBack={navigation.canGoBack()}
/>
<MenuButton
onPress={() => {
navigation.toggleDrawer();
}}
/>
</View>
),
headerTitle: () => (
<HeaderTitle navigation={navigation} route={route} />
),
headerRight: () => (
<View style={styles.headerRightContainer}>
<TouchableOpacity style={styles.searchButton}>
<IconSearch stroke={R.colors.white} width={24} height={24} />
</TouchableOpacity>
<View style={styles.avatarButton} />
</View>
),
headerStyle: {
backgroundColor: R.colors.blue500,
},
})}
>
<Drawer.Screen
name={ScreenName.CLASSSCHEDULE}
component={ClassSchedule}
options={{
drawerLabel: () => null,
drawerIcon: () => null,
drawerItemStyle: { height: 0 },
}}
/>
<Drawer.Screen
name={ScreenName.FILTER3DATE}
component={Filter3Date}
options={{
drawerLabel: () => null,
drawerIcon: () => null,
drawerItemStyle: { height: 0 },
}}
/>
<Drawer.Screen
name={ScreenName.FILTERDATE}
component={FilterDateView}
options={{
drawerItemStyle: { height: 0 },
}}
/>
<Drawer.Screen
name={ScreenName.FILTERWEEK}
component={FilterWeekView}
options={{
drawerItemStyle: { height: 0 },
}}
/>
</Drawer.Navigator>
);
};
const mapStateToProps = (state) => ({
user: state.userReducer,
});
const styles = StyleSheet.create({
headerBackButton: {
padding: 10,
marginLeft: 10,
borderRadius: 8,
},
menuButton: {
padding: 10,
marginLeft: 5,
},
headerLeftContainer: {
flexDirection: "row",
alignItems: "center",
},
headerRightContainer: {
flexDirection: "row",
alignItems: "center",
marginRight: 15,
},
searchButton: {
padding: 10,
marginRight: 10,
},
avatarButton: {
width: 30,
height: 30,
borderRadius: 30,
backgroundColor: R.colors.white,
},
headerTitleButton: {
alignItems: 'center',
justifyContent: 'center',
},
headerTitleText: {
fontSize: R.fontsize.fontsSize16,
color: R.colors.white,
fontFamily: R.fonts.InterMedium,
},
});
export default connect(mapStateToProps, {})(DrawerNavigatorView);
\ No newline at end of file
/**
* ENTRY_POINT: Drawer Navigation Module
* FUNCTIONALITY: Export chính cho drawer navigation system
* FEATURE: Tách biệt logic và giao diện, dễ maintain
*/
// COMPONENT: Import component chính
import DrawerNavigator from './drawerView';
// COMPONENT: Export các component con nếu cần sử dụng riêng
export { default as CustomDrawerContent } from './itemDrawer';
// EXPORT: Export mặc định là DrawerNavigator
export default DrawerNavigator;
/**
* USAGE EXAMPLE:
*
* // Sử dụng drawer navigator chính
* import DrawerNavigator from './drawer';
*
* // Hoặc sử dụng riêng CustomDrawerContent
* import { CustomDrawerContent } from './drawer';
*/
\ No newline at end of file
import React, { useState, useMemo, useCallback } from "react";
import {
DeviceEventEmitter,
View,
Text,
Image,
TouchableOpacity,
StyleSheet,
} from "react-native";
import {
DrawerContentScrollView,
useDrawerProgress,
} from "@react-navigation/drawer";
import R from "../../assets/R";
import Checkbox from "../../components/CheckBox";
import * as ScreenName from "../ScreenNames";
const IcDate = R.images.icDateDrawer;
const Ic3Date = R.images.ic3Date;
const IcWeek = R.images.icWeek;
const IcMonth = R.images.icMonth;
const CustomDrawerContent = (props) => {
const progress = useDrawerProgress();
const { navigation, state } = props;
const checkboxConfigs = useMemo(
() => [
{ key: "departmentCalendar", label: "Lịch phòng ban" },
{ key: "personalCalendar", label: "Lịch cá nhân" },
{ key: "teachingCalendar", label: "Lịch giảng dạy" },
{ key: "examCalendar", label: "Lịch coi thi" },
{ key: "examRoomCalendar", label: "Lịch sử dụng phòng thi" },
],
[]
);
const filterConfigs = useMemo(
() => [
{
label: "Theo ngày",
screenName: ScreenName.FILTERDATE,
icon: <IcDate stroke={'black'}/>
},
{
label: "3 ngày",
screenName: ScreenName.FILTER3DATE,
icon: <Ic3Date />
},
{
label: "Tuần",
screenName: ScreenName.FILTERWEEK,
icon: <IcWeek />
},
{
label: "Tháng",
screenName: ScreenName.CLASSSCHEDULE,
icon: <IcMonth />
},
],
[]
);
const [checkboxStates, setCheckboxStates] = useState({
departmentCalendar: false,
personalCalendar: false,
teachingCalendar: false,
examCalendar: false,
examRoomCalendar: false,
});
const currentRouteName = state.routes[state.index]?.name;
const handleCheckboxChange = useCallback((key, newValue) => {
setCheckboxStates((prev) => ({
...prev,
[key]: newValue,
}));
DeviceEventEmitter.emit("onCheckboxChange", { key, value: newValue });
}, []);
const handleScreenNavigation = useCallback((screenName) => {
navigation.navigate(screenName);
navigation.closeDrawer();
}, [navigation]);
const renderCheckbox = useCallback(
(config) => (
<Checkbox
key={config.key}
value={checkboxStates[config.key]}
onValueChange={(newValue) => handleCheckboxChange(config.key, newValue)}
label={config.label}
labelSpacing={30}
size={25}
labelSize={R.fontsize.fontsSize16}
checkedColor={R.colors.black}
marginBottom={20}
labelFontFamily={R.fonts.InterRegular}
labelWeight={400}
/>
),
[checkboxStates, handleCheckboxChange]
);
const renderFilterItem = useCallback(
(item) => {
const isSelected = currentRouteName === item.screenName;
return (
<TouchableOpacity
key={item.screenName}
style={[
styles.filterItem,
isSelected && styles.filterItemSelected,
]}
onPress={() => handleScreenNavigation(item.screenName)}
activeOpacity={0.7}
>
{item.icon}
<Text style={[
styles.filterLabel,
isSelected && styles.filterLabelSelected
]}>
{item.label}
</Text>
</TouchableOpacity>
);
},
[currentRouteName, handleScreenNavigation]
);
return (
<DrawerContentScrollView
{...props}
contentContainerStyle={styles.scrollContainer}
>
<View style={styles.logoContainer}>
<Image
source={R.images.igLogo}
style={styles.logo}
resizeMode="contain"
/>
</View>
<View style={styles.filterSection}>
{filterConfigs.map(renderFilterItem)}
</View>
<View style={styles.divider} />
<View style={styles.checkboxSection}>
<Text style={styles.sectionTitle}>Loi lch</Text>
{checkboxConfigs.map(renderCheckbox)}
</View>
</DrawerContentScrollView>
);
};
const styles = StyleSheet.create({
scrollContainer: {
flexGrow: 1,
paddingBottom: 20,
},
logoContainer: {
alignItems: "center",
marginVertical: 20,
},
logo: {
width: 49,
height: 24,
},
filterSection: {
paddingHorizontal: 0,
},
filterItem: {
flexDirection: "row",
alignItems: "center",
paddingVertical: 10,
paddingHorizontal: 15,
marginVertical: 4,
},
filterItemSelected: {
backgroundColor: R.colors.blue100,
borderRadius: 50,
marginHorizontal: 15,
},
filterLabel: {
marginLeft: 30,
fontSize: R.fontsize.fontsSize16,
color: R.colors.black,
flex: 1,
fontFamily: R.fonts.InterMedium,
fontWeight: '400',
},
filterLabelSelected: {
color: R.colors.blue,
fontFamily: R.fonts.InterSemiBold,
},
divider: {
height: 1,
backgroundColor: R.colors.black,
marginHorizontal: 15,
marginTop: 21,
},
checkboxSection: {
paddingHorizontal: 15,
marginTop: 24,
},
sectionTitle: {
fontWeight: "400",
fontFamily: R.fonts.InterMedium,
marginBottom: 10,
fontSize: R.fontsize.fontsSize16,
color: R.colors.black,
},
});
export default CustomDrawerContent;
\ No newline at end of file
...@@ -158,7 +158,7 @@ const styles = StyleSheet.create({ ...@@ -158,7 +158,7 @@ const styles = StyleSheet.create({
width: 60, width: 60,
justifyContent: 'center', justifyContent: 'center',
alignItems: 'center', alignItems: 'center',
backgroundColor: '#D7E6FF', backgroundColor: R.colors.white,
borderRadius: 10, borderRadius: 10,
}, },
txt: { txt: {
......
export const LOGINSCREEN = 'LOGINSCREEN';
export const HOMESCREEN = 'HOMESCREEN'; export const HOMESCREEN = 'HOMESCREEN';
export const TABNAVIGATOR = 'TABNAVIGATOR'; export const TABNAVIGATOR = 'TABNAVIGATOR';
export const ENTER_PASSWORD = 'ENTER_PASSWORD'; export const ELEARNINGSCREEN = 'ELEARNINGSCREEN';
export const CONFIRM_OTP = 'CONFIRM_OTP'; export const EXAMSCHEDULE = 'EXAMSCHEDULE';
export const CHANGE_PHONE_NUMBER_OTP = 'CHANGE_PHONE_NUMBER_OTP'; export const QRCODESCREEN = 'QRCODESCREE';
export const NOTIFICATIONSCREEN = 'NOTIFICATIONSCREEN';
export const PROFILESCREEN = 'PROFILESCREEN';
export const NEWSDETAILS = 'NEWSDETAILS';
export const ROLLCALL = 'ROLLCALL';
export const TRAININGPROGRAM = 'TRAININGPROGRAM';
export const DEBT = 'DEBT';
export const CERTIFICATEREGISTRATION = 'CERTIFICATEREGISTRATION';
export const LISTCERTIFICATE = 'LISTCERTIFICATE';
export const DETAILCERTIFICATE = 'DETAILCERTIFICATE';
export const DETAILROLLCALL = 'DETAILROLLCALL';
export const TRAININGPOINT = 'TRAININGPOINT';
export const PROFILE = 'PROFILE';
export const MEDICAL = 'MEDICAL';
export const OUTPATIENTINFOMATION = 'OUTPATIENTINFOMATION';
export const DRAWERNAVIGATION = 'DRAWERNAVIGATION';
export const CLASSSCHEDULE = 'CLASSSCHEDULE';
export const FILTERDATE = 'FILTERDATE';
export const FILTERWEEK = 'FILTERWEEK';
export const FILTER3DATE = 'FILTER3DATE';
export const DETAILCLASSSCHEDULE = 'DETAILCLASSSCHEDULE';
\ No newline at end of file
...@@ -2,9 +2,26 @@ import React, {Fragment, useRef, useEffect, useState} from 'react'; ...@@ -2,9 +2,26 @@ import React, {Fragment, useRef, useEffect, useState} from 'react';
import {NavigationContainer} from '@react-navigation/native'; import {NavigationContainer} from '@react-navigation/native';
import {createStackNavigator} from '@react-navigation/stack'; import {createStackNavigator} from '@react-navigation/stack';
import TabNavigator from './TabNavigation'; import TabNavigator from './TabNavigation';
import Login from '../screens/login'; import Home from '../screens/home';
import Elearning from '../screens/elearning';
import QrCode from '../screens/qrcode'
import Notification from '../screens/notification'
import Profile from '../screens/profile'
import NewDetails from '../screens/news_details';
import RollCall from '../screens/roll_call';
import TrainingProgram from '../screens/training_program';
import CertificateRegistration from '../screens/certificate_registration';
import ListCertificate from '../screens/certificate_registration/list';
import DetailCertificate from '../screens/certificate_registration/detail';
import DetailRollCall from '../screens/roll_call/detail';
import TrainingPoint from '../screens/training_point'
import Medical from '../screens/medical';
import DrawerNav from './Drawer/index'
import OutpatientInfomation from '../screens/outpatient_information';
import * as ScreenName from './ScreenNames'; import * as ScreenName from './ScreenNames';
import Debt from '../screens/debt';
import DetailClassSchedule from '../screens/class_schedule/detail';
import ExamSchedule from '../screens/exam_schedule';
const Stack = createStackNavigator(); const Stack = createStackNavigator();
function MyStack(props) { function MyStack(props) {
...@@ -14,13 +31,32 @@ function MyStack(props) { ...@@ -14,13 +31,32 @@ function MyStack(props) {
headerStatusBarHeight: 0, headerStatusBarHeight: 0,
}} }}
headerMode={'none'} headerMode={'none'}
initialRouteName={ScreenName.LOGINSCREEN}> initialRouteName={ScreenName.TABNAVIGATOR}>
<Stack.Screen name={ScreenName.LOGINSCREEN} component={Login} /> <Stack.Screen name = {ScreenName.HOMESCREEN} component = {Home}/>
<Stack.Screen name={ScreenName.TABNAVIGATOR} component={TabNavigator} /> <Stack.Screen name={ScreenName.TABNAVIGATOR} component={TabNavigator} />
<Stack.Screen name={ScreenName.DRAWERNAVIGATION} component={DrawerNav} />
<Stack.Screen name={ScreenName.ELEARNINGSCREEN} component={Elearning}/>
<Stack.Screen name={ScreenName.QRCODESCREEN} component={QrCode}/>
<Stack.Screen name={ScreenName.NOTIFICATIONSCREEN} component={Notification}/>
<Stack.Screen name={ScreenName.PROFILESCREEN} component={Profile}/>
<Stack.Screen name={ScreenName.NEWSDETAILS} component={NewDetails}/>
<Stack.Screen name={ScreenName.ROLLCALL} component={RollCall}/>
<Stack.Screen name={ScreenName.TRAININGPROGRAM} component={TrainingProgram}/>
<Stack.Screen name={ScreenName.DEBT} component={Debt}/>
<Stack.Screen name={ScreenName.CERTIFICATEREGISTRATION} component={CertificateRegistration}/>
<Stack.Screen name={ScreenName.LISTCERTIFICATE} component={ListCertificate}/>
<Stack.Screen name={ScreenName.DETAILCERTIFICATE} component={DetailCertificate}/>
<Stack.Screen name={ScreenName.DETAILROLLCALL} component={DetailRollCall}/>
<Stack.Screen name={ScreenName.TRAININGPOINT} component={TrainingPoint}/>
<Stack.Screen name={ScreenName.PROFILE} component={Profile}/>
<Stack.Screen name={ScreenName.MEDICAL} component={Medical}/>
<Stack.Screen name={ScreenName.OUTPATIENTINFOMATION} component={OutpatientInfomation}/>
<Stack.Screen name={ScreenName.DETAILCLASSSCHEDULE} component={DetailClassSchedule}/>
<Stack.Screen name={ScreenName.EXAMSCHEDULE} component={ExamSchedule}/>
</Stack.Navigator> </Stack.Navigator>
); );
} }
export default function App(props) { export default function App(props) {
return ( return (
<Fragment> <Fragment>
......
import React, {useEffect, useState} from 'react'; import React, { useEffect, useState } from 'react';
import {DeviceEventEmitter, Image, View} from 'react-native'; import { DeviceEventEmitter, Image, View } from 'react-native';
import {createBottomTabNavigator} from '@react-navigation/bottom-tabs'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import i18n from '../helper/i18/i18n'; import i18n from '../helper/i18/i18n';
import {connect} from 'react-redux'; import { connect } from 'react-redux';
import R from '../assets/R'; import R from '../assets/R';
import Account from '../screens/account';
import Home from '../screens/home'; import Home from '../screens/home';
import ELearning from '../screens/elearning';
import QrCode from '../screens/qrcode';
import Notification from '../screens/notification';
import Profile from '../screens/profile';
const Tab = createBottomTabNavigator(); const Tab = createBottomTabNavigator();
const TabNavigator = props => { const TabNavigator = props => {
const [reload, setReload] = useState(false); const [reload, setReload] = useState(false);
useEffect(() => { useEffect(() => {
let setLanguage = DeviceEventEmitter.addListener('setLanguage', value => { let setLanguage = DeviceEventEmitter.addListener('setLanguage', value => {
setReload(!reload); setReload(!reload);
...@@ -24,50 +26,85 @@ const TabNavigator = props => { ...@@ -24,50 +26,85 @@ const TabNavigator = props => {
return ( return (
<Tab.Navigator <Tab.Navigator
initialRouteName="Screen5" initialRouteName="Screen5"
screenOptions={{headerShown: false}} screenOptions={{
tabBarOptions={{ headerShown: false,
showIcon: true, tabBarStyle: {
showLabel: true, height: 61, // 👈 Tăng chiều cao ở đây
activeTintColor: R.colors.main, paddingVertical: 5, // 👈 Tăng padding nếu icon/text bị sát mép dưới
},
style: { tabBarActiveTintColor: R.colors.blue500, // active icon/text
borderTopLeftRadius: 24, tabBarShowLabel: true,
borderTopRightRadius: 24, tabBarLabelStyle: {
shadowColor: '#000', fontSize: R.fontsize.fontsSize10,
shadowOffset: { fontWeight: "400",
width: 0, fontFamily: R.fonts.InterRegular,
height: 2,
},
shadowOpacity: 0.29,
shadowRadius: 2,
elevation: 7,
justifyContent: 'center',
}, },
}}>
tabBarShowIcon: true,
}}
>
<Tab.Screen <Tab.Screen
name="HomeScreen1" name="HomeScreen1"
component={Home} component={Home}
options={{ options={{
tabBarLabel: i18n.t('Home'), tabBarLabel: i18n.t('Home'),
tabBarIcon: ({color, size}) => ( tabBarIcon: ({ focused }) => (
<Image <Image
source={R.images.icHome} source={focused ? R.images.icHomeSel : R.images.icHomeUnSel}
style={{width: size, height: size, tintColor: color}} style={{ width: '50%', height: '50%', resizeMode: 'contain' }}
/> />
), ),
}} }}
/> />
<Tab.Screen <Tab.Screen
name="AccountScreen" name="Elearning"
component={Account} component={ELearning}
options={{
tabBarLabel: i18n.t('Elearning'),
tabBarIcon: ({ focused }) => (
<Image
source={focused ? R.images.icELearing : R.images.icELearingUnSel}
style={{ width: '50%', height: '50%', resizeMode: 'contain' }}
/>
),
}}
/>
<Tab.Screen
name="QRScreen"
component={QrCode}
options={{
tabBarLabel: i18n.t('QrCode'),
tabBarIcon: ({ focused }) => (
<Image
source={focused ? R.images.icQrCode : R.images.icQrCodeUnSel}
style={{ width: '70%', height: '70%', resizeMode: 'contain' }}
/>
),
}}
/>
<Tab.Screen
name="Notification"
component={Notification}
options={{
tabBarLabel: i18n.t('Notification'),
tabBarIcon: ({ focused }) => (
<Image
source={focused ? R.images.icNotification : R.images.icNotificationUnSel}
style={{ width: '50%', height: '50%', resizeMode: 'contain' }}
/>
),
}}
/>
<Tab.Screen
name="Profile"
component={Profile}
options={{ options={{
tabBarLabel: i18n.t('Account'), tabBarLabel: i18n.t('Profile'),
tabBarIcon: ({color, size}) => ( tabBarIcon: ({ focused }) => (
<Image <Image
source={R.images.icAccount} source={focused ? R.images.icProfile : R.images.icProfileUn}
style={{width: size, height: size - 3, tintColor: color}} style={{ width: '50%', height: '50%', resizeMode: 'contain' }}
resizeMode={'contain'}
/> />
), ),
}} }}
......
import React, {useEffect, useState} from 'react';
import AccountView from './view';
import {useNavigation} from '@react-navigation/native';
import {connect} from 'react-redux';
import {saveUserToRedux, saveWalletInfo} from '../../actions/users';
import {showLoading, hideLoading} from '../../actions/loadingAction';
import {showAlert, TYPE} from '../../components/DropdownAlert';
const Account = props => {
return <AccountView />;
};
const mapStateToProps = state => {
return {
userInfo: state.userReducer.userInfo,
};
};
export default connect(mapStateToProps, {
saveUserToRedux,
saveWalletInfo,
showLoading,
hideLoading,
})(Account);
import React, {Component} from 'react';
import {View, Text, SafeAreaView, StyleSheet} from 'react-native';
import Header from '../../components/Header/Header';
import i18n from '../../helper/i18/i18n';
import R from '../../assets/R';
import PickerItem from '../../components/Picker/PickerItem';
import DatePicker from '../../components/Picker/PickerDate';
import PickerAvatar from '../../components/Picker/PickerAvatar';
import {WIDTH} from '../../config/Functions';
import AppText from '../../components/AppText';
const AccountView = props => {
return (
<SafeAreaView style={styles.container}>
<Header title={i18n.t('Account')} />
<View style={styles.body}>
<Text>{i18n.t('Account')}</Text>
</View>
</SafeAreaView>
);
};
export default AccountView;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: R.colors.white,
},
body: {
flex: 1,
paddingHorizontal: WIDTH(10),
},
});
import { StyleSheet, Text, View, TouchableOpacity, Image } from 'react-native';
import React from 'react';
import R from '../../assets/R';
const CardButtonImage = ({
onPress,
text = "Tải ảnh ở đây",
width,
height = 150,
disabled = false
}) => {
return (
<TouchableOpacity
style={[
styles.container_image,
{ width, height },
disabled && styles.disabled
]}
onPress={onPress}
disabled={disabled}
activeOpacity={0.7}
>
<View style={styles.image_placeholder}>
</View>
<View>
<Image
source={R.images.icImageDownload}
style={{width:20,height:20, marginRight:5,marginTop:5}}
/>
</View>
<Text style={styles.placeholder_text}>
{text}
</Text>
</TouchableOpacity>
);
};
export default CardButtonImage;
const styles = StyleSheet.create({
container_image: {
justifyContent: 'center',
alignItems: 'center',
marginHorizontal:15,
marginTop:3,
marginBottom:15,
borderWidth: 1,
borderColor: R.colors.blue500,
borderStyle: 'dashed',
borderRadius: 15,
backgroundColor: R.colors.white,
padding: 16,
flexDirection:'row',
flex:1,
},
image_placeholder: {
position: 'absolute',
width: '100%',
height: '100%',
borderRadius: 10,
},
placeholder_text: {
fontSize: R.fontsize.fontsSize14,
fontWeight: '500',
color:R.colors.black,
textAlign: 'center',
letterSpacing: 0.3,
},
disabled: {
opacity: 0.5,
backgroundColor: R.colors.gray400,
}
});
\ No newline at end of file
import React from 'react';
import {Text, View, StyleSheet} from 'react-native';
import DetailCertificateView from './view';
const DetailCertificate = (props) => {
return (
<DetailCertificateView />
);
};
export default DetailCertificate;
import React from "react";
import {
Text,
View,
TouchableOpacity,
StyleSheet,
Image,
SafeAreaView,
ScrollView,
} from "react-native";
import R from "../../../assets/R";
import Header from "../../../components/Header/Header";
const DetailCertificateView = (props) => {
return (
<SafeAreaView style={{flex:1, backgroundColor: R.colors.white}}>
<Header isBack={true} title={"Chứng chỉ toeic"} />
<ScrollView showsVerticalScrollIndicator={false} style={styles.container}>
<View style={styles.container_card}>
<View style={styles.status}>
<Text style={[styles.text_title, { color: R.colors.white }]}>
Chng ch đang được đánh giá
</Text>
</View>
<View style={styles.card_item}>
<View style={styles.header_card_item}>
<Text style={styles.text_title}>Chng ch toeic</Text>
</View>
<View style={styles.image_container}>
<Image
source={R.images.igProfileDemo}
style={styles.image}
/>
</View>
<View >
<Text style={styles.sub_text}>Loi chng ch: TOEIC</Text>
<Text style={styles.sub_text}>Ngày sinh: 23/10/2004</Text>
<Text style={styles.sub_text}>
CCCD/CMND đăng ký: 00228956325
</Text>
<Text style={styles.sub_text}>Ngày thi: 23/10/2024</Text>
<Text style={styles.sub_text}>Tng đim: 875</Text>
<View
style={{
flexDirection: "row",
justifyContent: "space-between",
}}
>
<Text style={styles.sub_text}>Đim nói: 0</Text>
<Text style={styles.sub_text}>Đim nghe: 445</Text>
</View>
<View
style={{
flexDirection: "row",
justifyContent: "space-between",
}}
>
<Text style={styles.sub_text}>Đim đọc: 430</Text>
<Text style={styles.sub_text}>Đim viết: 0</Text>
</View>
<Text style={styles.sub_text}>S TRF: 085692265852</Text>
<Text style={styles.sub_text}>Có hiu lc đến: 23/10/2026</Text>
<Text style={[styles.sub_text, { color: R.colors.main }]}>
Không dùng để đăng ký hc bng
</Text>
</View>
</View>
</View>
</ScrollView>
</SafeAreaView>
);
};
export default DetailCertificateView;
const styles = StyleSheet.create({
container: {
backgroundColor:R.colors.white,
paddingBottom: 10,
},
image: {
maxWidth: '100%',
maxHeight: 200,
resizeMode: "contain",
},
image_container: {
maxWidth: 340,
maxHeight: 200,
alignSelf: "center",
},
status: {
backgroundColor: R.colors.yellow,
borderTopLeftRadius: 15,
borderTopRightRadius: 15,
paddingLeft: 13,
paddingVertical: 5
},
container_card: {
marginVertical: 15,
borderRadius: 15,
marginHorizontal: 15,
backgroundColor: R.colors.white,
shadowColor: R.colors.black,
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 1,
shadowRadius: 2,
elevation: 2,
},
card_item: {
backgroundColor: R.colors.white,
borderBottomLeftRadius: 15,
borderBottomRightRadius: 15,
paddingHorizontal: 15,
paddingVertical: 10,
},
header_card_item: {
justifyContent: "space-between",
flexDirection: "row",
},
text_title: {
fontFamily: R.fonts.InterRegular,
fontSize: R.fontsize.fontsSize14,
fontWeight: "400",
color: R.colors.black,
},
sub_text: {
fontFamily: R.fonts.InterRegular,
fontSize: R.fontsize.fontsSize12,
fontWeight: "300",
lineHeight: 24,
color: R.colors.black,
},
});
import React, { useState ,useMemo} from 'react';
import {Text, View, StyleSheet} from 'react-native';
import CertificateRegistrationView from './view';
const CertificateRegistration = (props) => {
const [titleHeader,setTitleHeader]= useState('CertificateRegisitor')
const [isSelected, setSelection] = useState(false);
const [selectedValue, setSelectedValue] = useState(null);
const [dataListCertificate, setdataListCertificate]= useState([
{
id: 1,
user_info: {
full_name: 'Nguyễn Văn A',
date: '27/11/2004',
identity_card: '0129210102',
},
certificates: [
{
id_certificate: 'cer_1',
type_certificate: 'TOEIC',
status: 'Chờ duyệt',
registered_date: '2025-07-01',
listening_point: '9',
speaking_point:'9',
reading_point:'9',
writing_score:'9',
total_score:'9',
date_exam:'27/02/2021',
date_effective:'28/09/2023',
code_certificate :'192381832183',
image_certificate:'https://sf-static.upanhlaylink.com/img/image_202507281a9a3a8e0a76ab471f9415ee21d9c57e.jpg'
},
],
apply_for_ascholarship: false
},
{
id: 2,
user_info: {
full_name: 'Nguyễn Văn A',
date: '27/11/2004',
identity_card: '0129210102',
},
certificates: [
{
id_certificate: 'cer_1',
type_certificate: 'TOEIC',
status: 'Chờ duyệt',
registered_date: '2025-07-01',
listening_point: '9',
speaking_point:'9',
reading_point:'9',
writing_score:'9',
total_score:'9',
date_exam:'27/02/2021',
date_effective:'28/09/2023',
code_certificate :'192381832183',
image_certificate:'https://sf-static.upanhlaylink.com/img/image_202507281a9a3a8e0a76ab471f9415ee21d9c57e.jpg'
},
],
apply_for_ascholarship: false
},
])
const dropdownData = useMemo(() => {
return dataListCertificate.flatMap(user =>
user.certificates.map(certificate => ({
label: `${user.user_info.full_name} - ${certificate.status}`,
value: user.id,
originalCertificate: certificate,
originalUser: user.user_info
}))
);
}, [dataListCertificate]);
const handleSelect = (item) => {
console.log("Đã chọn:", item);
setSelectedValue(item.value);
};
return (
<CertificateRegistrationView
titleHeader={titleHeader}
dataList={dropdownData}
value={selectedValue}
onSelect={handleSelect}
isSelected={isSelected}
setSelection={setSelection}
/>
);
};
export default CertificateRegistration;
import React from 'react';
import {Text, View, StyleSheet} from 'react-native';
import ListCertificateView from './view';
const ListCertificate = (props) => {
return (
<ListCertificateView />
);
};
export default ListCertificate;
import { Image, StyleSheet, Text, View } from 'react-native'
import React from 'react'
import R from '../../../assets/R'
import Button from '../../../components/Button'
import { useNavigation } from "@react-navigation/native";
import { DETAILCERTIFICATE } from "../../../routers/ScreenNames";
const ItemNav = () => {
const navigation = useNavigation();
return (
<View style={styles.container_card}>
<View style={styles.header_card_item}>
<Text style={styles.text_title_left}>Chng ch toeic</Text>
<Text
onPress={() => navigation.navigate(DETAILCERTIFICATE)}
style={styles.text_title_right}>Chi tiết</Text>
</View>
<View style={styles.image_container}>
<Image
source={R.images.igProfileDemo}
style={styles.image}
/>
</View>
<View style={styles.footer_card_item}>
<View>
<Text style={styles.sub_text}>Trng thái: Đang đánh giá</Text>
<Text style={styles.sub_text}>Ngày đăng ký: 23/10/2024</Text>
<Text style={[styles.sub_text, { color: R.colors.blue500 }]}>Không dùng để đăng ký hc bng</Text>
</View>
<View style={styles.button_container}>
<Button
title={"Huỷ"}
fontFamily={R.fonts.InterRegular}
fontWeight={300}
fontSize={14}
width={61}
height={27}
backgroundColor={R.colors.red}
borderRadius={10}
textColor={R.colors.white}
/>
</View>
</View>
</View>
)
}
export default ItemNav
const styles = StyleSheet.create({
container_card: {
borderRadius: 15,
backgroundColor: R.colors.white,
shadowColor: R.colors.black,
marginVertical: 5,
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 1,
shadowRadius: 2,
elevation: 2,
paddingHorizontal: 15,
paddingVertical: 10,
},
button_container: {
alignSelf: "flex-end",
},
header_card_item: {
justifyContent: "space-between",
flexDirection: "row",
},
footer_card_item: {
justifyContent: "space-between",
flexDirection: "row",
},
image: {
maxWidth: '100%',
maxHeight: 200,
resizeMode: "contain",
},
image_container: {
maxWidth: 340,
maxHeight: 200,
alignSelf: "center",
},
text_title_left: {
fontFamily: R.fonts.InterRegular,
fontSize: R.fontsize.fontsSize14,
fontWeight: '400',
color: R.colors.black
},
text_title_right: {
fontFamily: R.fonts.InterRegular,
fontSize: R.fontsize.fontsSize14,
fontWeight: '400',
textDecorationLine: 'underline',
color: R.colors.main
},
sub_text: {
fontFamily: R.fonts.InterRegular,
fontSize: R.fontsize.fontsSize12,
fontWeight: "300",
color: R.colors.black
}
})
\ No newline at end of file
import React from "react";
import { useNavigation } from "@react-navigation/native";
import {
Text,
View,
StyleSheet,
ScrollView,
SafeAreaView,
} from "react-native";
import Header from "../../../components/Header/Header";
import Button from "../../../components/Button";
import R from "../../../assets/R";
import ItemNav from "./item";
import { CERTIFICATEREGISTRATION } from "../../../routers/ScreenNames";
const ListCetificateView = (props) => {
const { } = props;
const navigate = useNavigation();
return (
<SafeAreaView style={{flex:1, backgroundColor: R.colors.white}}>
<Header isBack={true} isSearch={true} title={"Danh sách chứng chỉ"}
/>
<ScrollView style={styles.container}>
<View style={styles.container_header}>
<Text style={styles.text}>Các chng ch đã đăng kí</Text>
<Button
onPress={() => navigate.navigate(CERTIFICATEREGISTRATION)}
height={27}
borderRadius={10}
textColor={R.colors.white}
fontFamily={R.fonts.InterRegular}
fontWeight={300}
fontSize={R.fontsize.fontsSize12}
iconSpacingLeft={2}
paddingHorizontal={10}
icon={R.images.icPlus}
backgroundColor={R.colors.blue500}
title={"Thêm mới"}
/>
</View>
<View style={styles.container_item}>
<ItemNav />
</View>
</ScrollView>
</SafeAreaView>
);
};
export default ListCetificateView;
const styles = StyleSheet.create({
container: {
flex: 1,
paddingBottom: 10,
backgroundColor: R.colors.white,
},
container_header: {
alignItems: "center",
justifyContent: "space-between",
flexDirection: "row",
marginVertical: 10,
marginHorizontal: 15,
},
text: {
fontFamily: R.fonts.InterSemiBold,
color: R.colors.main,
},
container_item: {
marginHorizontal: 15,
},
});
import React from "react";
import {
Text,
View,
StyleSheet,
ScrollView,
} from "react-native";
import CheckBox from "../../components/CheckBox";
import Header from "../../components/Header/Header";
import I18n from "../../helper/i18/i18n";
import R from "../../assets/R";
import Button from "../../components/Button";
import CardButtonImage from "./card_button";
import DropdownSelect from "../../components/Dropdown/DropdownSel";
import CustomTextInput from "../../components/Input/TextFieldCus";
const CertificateRegistrationView = (props) => {
const { titleHeader, dataList, isSelected, setSelection } = props;
return (
<ScrollView
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.scrollContent}>
<View style={styles.container}>
<Header isBack title={I18n.t(titleHeader)} />
<DropdownSelect
title={"Loại chứng chỉ *"}
titleFontFamily={R.fonts.InterRegular}
titleFontSize={R.fontsize.fontsSize12}
titleFontWeight={"400"}
titleColor={R.colors.black}
data={dataList}
placeholder="HS"
placeholderFontFamily={R.fonts.InterRegular}
placeholderFontSize={R.fontsize.fontsSize12}
placeholderFontWeight={"400"}
placeholderColor={R.colors.grey}
borderRadius={10}
height={40}
marginHorizontal={15}
marginVertical={5}
iconColor={R.colors.black}
iconSize={10}
/>
<View style={{ marginHorizontal: 15, }}>
<CustomTextInput
title={"Ngày sinh "}
backgroundColor={R.colors.gray400}
/>
</View>
<View style={{ marginHorizontal: 15 }}>
<CustomTextInput
title={"CMND/CCCD (thí sinh dùng để đăng ký thi)"}
backgroundColor={R.colors.gray400}
/>
</View>
<View style={styles.container_row}>
<View style={{ flex: 1, marginLeft: 15 }}>
<CustomTextInput
title={"Điểm nghe "}
/>
</View>
<View style={{ width: '3%' }}></View>
<View style={{ flex: 1, marginRight: 15 }}>
<CustomTextInput
title={"Điểm nói "}
/>
</View>
</View>
<View style={styles.container_row}>
<View style={{ flex: 1, marginLeft: 15 }}>
<CustomTextInput
title={"Điểm đọc"}
/>
</View>
<View style={{ width: '3%' }}></View>
<View style={{ flex: 1, marginRight: 15 }}>
<CustomTextInput
title={"Điểm viết "}
/>
</View>
</View>
<View
style={styles.container_row}
>
<View style={{ flex: 0.85, marginBottom: 10, marginHorizontal: 15 }}>
<CustomTextInput
title={"Tổng điểm "}
/>
</View>
<CheckBox
label={"Đăng kí học bổng"}
titleFontFamily={R.fonts.InterRegular}
titleFontSize={R.fontsize.fontsSize12}
titleFontWeight={"300"}
borderRadius={30}
size={15}
labelStyle={{
fontSize: R.fontsize.fontsSize12,
fontWeight: "300",
color: R.colors.black,
fontFamily: R.fonts.InterRegular,
}}
style={{ marginTop: 15, flex: 1 }}
/>
</View>
<View style={styles.container_row_body}>
<CustomTextInput
title={"Ngày thi "}
/>
</View>
<View style={styles.container_row_body}>
<CustomTextInput
title={"Có hiệu lực đến"}
/>
</View>
<View style={styles.container_row_body}>
<CustomTextInput
title={"Số TRF"}
/>
</View>
<Text style={{ marginHorizontal: 15, color: R.colors.black }}>
nh chng ch <Text style={{ color: "red" }}>*</Text>
</Text>
<CardButtonImage />
<View style={styles.footer}>
<CheckBox
value={isSelected}
onValueChange={setSelection}
borderRadius={10}
marginLeft={15}
/>
<Text style={styles.text_footer}>
"Tôi xin cam đoan rằng chứng chỉ mà tôi đã nộp là chứng chỉ hoàn
toàn hợp lệ và có giá trị pháp lý. Nếu phát hiện bất kỳ sai phạm
nào liên quan đến tính xác thực của chứng chỉ, tôi xin hoàn toàn
chịu trách nhiệm và chấp nhận mọi hình thức xử lý theo quy định,
bao gồm cả việc bị buộc thôi học và cấm thi vào trường Đại học
Công nghệ Thông tin (UIT) trong thời hạn 5 năm."
</Text>
</View>
<Button
title="Thanh toán"
marginHorizontal={15}
fontSize={R.fontsize.fontSizeContent}
fontWeight={300}
fontFamily={R.fonts.InterRegular}
textStyle={styles.text_button}
backgroundColor={R.colors.buttonColorSel}
textColor={R.colors.white}
borderRadius={10}
paddingVertical={4}
/>
</View>
</ScrollView>
);
};
export default CertificateRegistrationView;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: R.colors.white,
},
container_row: {
flexDirection: 'row'
},
container_row_body: {
marginHorizontal: 15,
},
footer: {
flexDirection: "row",
marginBottom: 84,
marginRight: 15
},
text_footer: {
marginLeft: 10,
flex: 1,
fontFamily: R.fonts.InterRegular,
fontSize: R.fontsize.fontsSize12,
color: R.colors.black,
fontWeight: '400'
}
});
import React from 'react';
import {Text, View, StyleSheet} from 'react-native';
import DetailClassScheduleView from './view';
const DetailClassSchedule = (props) => {
return (
<DetailClassScheduleView />
);
};
export default DetailClassSchedule;
import React from 'react';
import { Text, View, TouchableOpacity, StyleSheet, ImageBackground } from 'react-native';
import R from '../../../assets/R';
import { useNavigation } from '@react-navigation/native';
const DetailClassScheduleView = (props) => {
const { } = props;
const ArrowLeftIcon = R.images.icBack;
const navigate = useNavigation();
return (
<SafeAreaView
style={styles.container}>
<ImageBackground
source={R.images.igBackgroundSlider}
style={styles.background_header}
>
<TouchableOpacity style={styles.back_button} onPress={() => navigate.goBack()}>
<ArrowLeftIcon
/>
</TouchableOpacity>
</ImageBackground>
<View style={styles.container_content}>
<Text style={styles.text_title}>Lch dy lp IT0032.47.T1</Text>
<Text style={styles.text_content}>Th 6: 25/07/2025, 07:00 - 09:00</Text>
<View style={[styles.container_content, { paddingHorizontal: 15, paddingVertical: 5 }]}>
<Text style={styles.text_content}>V trí: Phòng B205</Text>
<Text style={styles.text_content}>Sĩ s: 40</Text>
<Text style={styles.text_content}>Lp hc: Thc hành</Text>
<Text style={styles.text_content}>Hình thc hc: Trc tiếp</Text>
</View>
</View>
</SafeAreaView>
);
};
export default DetailClassScheduleView;
const styles = StyleSheet.create({
container: {
flex: 1,
},
back_button: {
position: 'absolute',
top: 20,
left: 15,
},
background_header: {
width: '100%',
height: 178,
},
text_title: {
fontSize: R.fontsize.fontsSize18,
fontWeight: '500',
fontFamily: R.fonts.InterRegular,
color: R.colors.blue500,
},
container_content: {
padding: 15,
},
text_content: {
fontSize: R.fontsize.fontsSize12,
fontWeight: '500',
fontFamily: R.fonts.InterSemiBold,
color: R.colors.black,
},
})
\ No newline at end of file
import React from 'react';
import {Text, View, StyleSheet} from 'react-native';
import Filter3DateView from './view';
const Filter3Date = (props) => {
return (
<Filter3DateView />
);
};
export default Filter3Date;
import React, {useState, useRef, useEffect} from 'react';
import {
View,
Text,
TouchableOpacity,
ScrollView,
StyleSheet,
Dimensions,
SafeAreaView,
DeviceEventEmitter,
PanResponder,
} from 'react-native';
import R from '../../../assets/R';
const {width: screenWidth, height: screenHeight} = Dimensions.get('window');
const HOUR_HEIGHT = 80;
const DAY_COLUMN_WIDTH = (screenWidth - 70) / 3;
const Filter3DateView = ({navigation}) => {
const [currentDate, setCurrentDate] = useState(new Date(2025, 6, 24));
const [selectedDate, setSelectedDate] = useState(new Date(2025, 6, 24));
const [showMonthPicker, setShowMonthPicker] = useState(false);
const scrollViewRef = useRef(null);
useEffect(() => {
DeviceEventEmitter.emit('onDateChange', selectedDate);
}, [selectedDate]);
useEffect(() => {
DeviceEventEmitter.emit('onDateChange', currentDate);
}, [currentDate]);
const createMockEvents = () => {
return [
{
id: '1',
title: 'Lịch vào trực lớp TTCĐT 445.T1',
subtitle: 'CS lớp 4D',
time: '07:00',
endTime: '09:00',
date: '2025-07-24',
type: 'class',
},
{
id: '2',
title: 'Meeting team development',
subtitle: 'Phòng họp A1',
time: '10:00',
endTime: '11:30',
date: '2025-07-25',
type: 'meeting',
},
{
id: '3',
title: 'Training React Native',
subtitle: 'Online Zoom',
time: '14:00',
endTime: '16:00',
date: '2025-07-26',
type: 'training',
},
{
id: '4',
title: 'Code Review Session',
subtitle: 'Dev Team',
time: '09:30',
endTime: '11:30',
date: '2025-07-24',
type: 'review',
},
];
};
const mockEvents = createMockEvents();
const formatDateToString = (date) => {
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
return `${year}-${month}-${day}`;
};
const getEventsForDate = (date) => {
const dateStr = formatDateToString(date);
return mockEvents.filter(event => event.date === dateStr);
};
const getDayName = (date) => {
const days = ['Chủ nhật', 'Thứ 2', 'Thứ 3', 'Thứ 4', 'Thứ 5', 'Thứ 6', 'Thứ 7'];
return days[date.getDay()];
};
const getMonthName = (monthIndex) => {
const months = [
'Tháng 1', 'Tháng 2', 'Tháng 3', 'Tháng 4', 'Tháng 5', 'Tháng 6',
'Tháng 7', 'Tháng 8', 'Tháng 9', 'Tháng 10', 'Tháng 11', 'Tháng 12',
];
return months[monthIndex];
};
const get3DaysDates = (date) => {
const dates = [];
for (let i = 0; i < 3; i++) {
const dayDate = new Date(date);
dayDate.setDate(date.getDate() + i);
dates.push(dayDate);
}
return dates;
};
const isToday = (date) => {
const today = new Date();
return (
date.getDate() === today.getDate() &&
date.getMonth() === today.getMonth() &&
date.getFullYear() === today.getFullYear()
);
};
const handleMonthSelect = (monthIndex) => {
const newDate = new Date(currentDate);
newDate.setMonth(monthIndex);
setCurrentDate(newDate);
setSelectedDate(newDate);
setShowMonthPicker(false);
DeviceEventEmitter.emit('onDateChange', newDate);
};
const swipeToNext3Days = () => {
const nextDate = new Date(currentDate);
nextDate.setDate(currentDate.getDate() + 3);
setCurrentDate(nextDate);
setSelectedDate(nextDate);
DeviceEventEmitter.emit('onDateChange', nextDate);
};
const swipeToPrev3Days = () => {
const prevDate = new Date(currentDate);
prevDate.setDate(currentDate.getDate() - 3);
setCurrentDate(prevDate);
setSelectedDate(prevDate);
DeviceEventEmitter.emit('onDateChange', prevDate);
};
const toggleMonthPicker = () => {
setShowMonthPicker(!showMonthPicker);
};
const panResponder = PanResponder.create({
onMoveShouldSetPanResponder: (evt, gestureState) => {
return Math.abs(gestureState.dx) > 30 && Math.abs(gestureState.dy) < 100;
},
onPanResponderMove: (evt, gestureState) => {},
onPanResponderRelease: (evt, gestureState) => {
if (gestureState.dx > 50) {
swipeToPrev3Days();
} else if (gestureState.dx < -50) {
swipeToNext3Days();
}
},
});
const calculateEventPosition = (startTime, endTime) => {
const [startHour, startMinute] = startTime.split(':').map(Number);
const [endHour, endMinute] = endTime.split(':').map(Number);
const startTotalMinutes = startHour * 60 + startMinute;
const endTotalMinutes = endHour * 60 + endMinute;
const durationMinutes = endTotalMinutes - startTotalMinutes;
const topPosition = (startTotalMinutes / 60) * HOUR_HEIGHT;
const height = (durationMinutes / 60) * HOUR_HEIGHT;
return { topPosition, height };
};
const renderMonthPicker = () => {
if (!showMonthPicker) return null;
return (
<View style={styles.monthPickerContainer}>
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
contentContainerStyle={styles.monthPickerContent}>
{Array.from({length: 12}, (_, index) => (
<TouchableOpacity
key={index}
style={[
styles.monthItem,
currentDate.getMonth() === index && styles.monthItemSelected,
]}
onPress={() => handleMonthSelect(index)}>
<Text
style={[
styles.monthItemText,
currentDate.getMonth() === index && styles.monthItemTextSelected,
]}>
{getMonthName(index)}
</Text>
</TouchableOpacity>
))}
</ScrollView>
</View>
);
};
const render3DaysHeader = () => {
const threeDaysDates = get3DaysDates(currentDate);
return (
<View style={styles.daysHeaderContainer}>
<View style={styles.timeColumnHeader} />
{threeDaysDates.map((date, index) => {
const isTodayDate = isToday(date);
return (
<View key={index} style={styles.dayHeaderCell}>
<Text style={[
styles.dayHeaderText,
isTodayDate && styles.todayHeaderText
]}>
{getDayName(date)}
</Text>
<View style={[
styles.dayNumberContainer,
isTodayDate && styles.todayNumberContainer
]}>
<Text style={[
styles.dayHeaderNumber,
isTodayDate && styles.todayHeaderNumber
]}>
{date.getDate()}
</Text>
</View>
</View>
);
})}
</View>
);
};
const renderTimeSlots = () => {
const hours = Array.from({length: 24}, (_, i) => i);
const threeDaysDates = get3DaysDates(currentDate);
return (
<View style={styles.timeSlotsContainer} {...panResponder.panHandlers}>
<ScrollView
ref={scrollViewRef}
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.scrollContent}>
<View style={styles.timelineContainer}>
<View style={styles.timeLabelsColumn}>
{hours.map((hour) => {
const timeStr = hour.toString().padStart(2, '0') + ':00';
return (
<View key={hour} style={styles.timeSlot}>
<Text style={styles.timeText}>{timeStr}</Text>
</View>
);
})}
</View>
<View style={styles.daysGridContainer}>
{threeDaysDates.map((date, dayIndex) => (
<View key={dayIndex} style={styles.dayColumn}>
{hours.map((hour) => (
<View key={hour} style={styles.gridCell} />
))}
{getEventsForDate(date).map((event) => {
const { topPosition, height } = calculateEventPosition(event.time, event.endTime);
return (
<TouchableOpacity
key={event.id}
style={[
styles.eventCard,
{
position: 'absolute',
top: topPosition,
height: Math.max(height, 40),
left: 4,
right: 4,
zIndex: 10,
backgroundColor: R.colors.main,
}
]}
activeOpacity={0.7}>
<Text style={styles.eventTitle} numberOfLines={height > 80 ? 3 : 2}>
{event.title}
</Text>
{height > 50 && (
<Text style={styles.eventSubtitle} numberOfLines={1}>
{event.subtitle}
</Text>
)}
<Text style={styles.eventTime}>
Thi gian hc: {event.time} - {event.endTime}, {getDayName(date)} {date.getDate()}/{date.getMonth() + 1}/{date.getFullYear()}
</Text>
</TouchableOpacity>
);
})}
</View>
))}
</View>
</View>
</ScrollView>
</View>
);
};
return (
<SafeAreaView style={styles.container}>
{renderMonthPicker()}
{render3DaysHeader()}
{renderTimeSlots()}
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: R.colors.white,
},
monthPickerContainer: {
backgroundColor: R.colors.white,
borderBottomWidth: 1,
borderBottomColor: R.colors.grey_200,
paddingVertical: 10,
},
monthPickerContent: {
paddingHorizontal: 15,
},
monthItem: {
paddingHorizontal: 20,
paddingVertical: 8,
marginRight: 10,
borderRadius: 20,
backgroundColor: R.colors.grey_50,
},
monthItemSelected: {
backgroundColor: R.colors.black500,
},
monthItemText: {
fontSize: R.fontsize.fontsSize12,
fontFamily: R.fonts.InterRegular,
color: R.colors.black,
},
monthItemTextSelected: {
color: R.colors.white,
fontFamily: R.fonts.InterMedium,
},
daysHeaderContainer: {
flexDirection: 'row',
backgroundColor: R.colors.grey_200,
borderBottomWidth: 1,
borderBottomColor: R.colors.grey_200,
paddingVertical: 12,
},
timeColumnHeader: {
width: 70,
},
dayHeaderCell: {
width: DAY_COLUMN_WIDTH,
alignItems: 'center',
justifyContent: 'center',
borderRightWidth: 1,
borderRightColor: R.colors.grey_200,
},
dayHeaderText: {
fontSize: R.fontsize.fontsSize10,
fontFamily: R.fonts.InterRegular,
color: R.colors.black,
},
todayHeaderText: {
color: R.colors.main,
fontFamily: R.fonts.InterMedium,
},
dayNumberContainer: {
minWidth: 28,
minHeight: 28,
borderRadius: 15,
alignItems: 'center',
justifyContent: 'center',
},
todayNumberContainer: {
backgroundColor: R.colors.blue500,
},
dayHeaderNumber: {
fontSize: R.fontsize.fontsSize14,
fontFamily: R.fonts.InterMedium,
color: R.colors.black,
},
todayHeaderNumber: {
color: R.colors.white,
fontFamily: R.fonts.InterSemiBold,
},
timeSlotsContainer: {
flex: 1,
backgroundColor: R.colors.white,
},
scrollContent: {
},
timelineContainer: {
flexDirection: 'row',
position: 'relative',
},
timeLabelsColumn: {
width: 70,
borderRightWidth: 1,
borderRightColor: R.colors.grey_200,
},
daysGridContainer: {
flex: 1,
flexDirection: 'row',
},
dayColumn: {
width: DAY_COLUMN_WIDTH,
position: 'relative',
borderRightWidth: 1,
borderRightColor: R.colors.grey_200,
},
timeSlot: {
height: HOUR_HEIGHT,
alignItems: 'center',
justifyContent: 'center',
borderBottomWidth: 1,
borderBottomColor: R.colors.grey_100,
},
gridCell: {
height: HOUR_HEIGHT,
borderBottomWidth: 1,
borderBottomColor: R.colors.grey_100,
width: '100%',
},
timeText: {
fontSize: R.fontsize.fontsSize12,
fontFamily: R.fonts.InterRegular,
color: R.colors.black,
},
eventCard: {
borderRadius: 8,
paddingHorizontal: 6,
paddingVertical: 4,
},
eventTitle: {
fontSize: R.fontsize.fontsSize8,
fontFamily: R.fonts.InterMedium,
color: R.colors.white,
marginBottom: 2,
},
eventSubtitle: {
fontSize: R.fontsize.fontsSize8,
fontFamily: R.fonts.InterRegular,
color: R.colors.white,
},
eventTime: {
fontSize: R.fontsize.fontsSize8,
fontFamily: R.fonts.InterRegular,
color: R.colors.white,
},
});
export default Filter3DateView;
\ No newline at end of file
import React, {useState, useRef, useEffect} from 'react';
import {DeviceEventEmitter, PanResponder} from 'react-native';
import FilterDateView from './view';
const FilterDate = ({navigation}) => {
const [currentDate, setCurrentDate] = useState(new Date(2025, 6, 24));
const [selectedDate, setSelectedDate] = useState(new Date(2025, 6, 24));
const [showMonthPicker, setShowMonthPicker] = useState(false);
const scrollViewRef = useRef(null);
useEffect(() => {
DeviceEventEmitter.emit('onDateChange', selectedDate);
}, [selectedDate]);
const createMockEvents = () => {
return [
{
id: '1',
title: 'Lịch vào trực lớp TTCĐT 445.T1',
subtitle: 'CS Địa lý 4D',
time: '07:00',
endTime: '09:00',
date: '2025-07-24',
type: 'class',
},
{
id: '2',
title: 'Meeting team development',
subtitle: 'Phòng họp A1',
time: '10:00',
endTime: '11:30',
date: '2025-07-24',
type: 'meeting',
},
{
id: '3',
title: 'Training React Native',
subtitle: 'Online Zoom',
time: '14:00',
endTime: '16:00',
date: '2025-07-25',
type: 'training',
},
];
};
const mockEvents = createMockEvents();
const formatDateToString = (date) => {
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
return `${year}-${month}-${day}`;
};
const getEventsForDate = (date) => {
const dateStr = formatDateToString(date);
return mockEvents.filter(event => event.date === dateStr);
};
const getDayName = (date) => {
const days = ['Chủ nhật', 'Thứ 2', 'Thứ 3', 'Thứ 4', 'Thứ 5', 'Thứ 6', 'Thứ 7'];
return days[date.getDay()];
};
const getMonthName = (monthIndex) => {
const months = [
'Tháng 1', 'Tháng 2', 'Tháng 3', 'Tháng 4', 'Tháng 5', 'Tháng 6',
'Tháng 7', 'Tháng 8', 'Tháng 9', 'Tháng 10', 'Tháng 11', 'Tháng 12',
];
return months[monthIndex];
};
const handleMonthSelect = (monthIndex) => {
const newDate = new Date(currentDate);
newDate.setMonth(monthIndex);
setCurrentDate(newDate);
setSelectedDate(newDate);
setShowMonthPicker(false);
DeviceEventEmitter.emit('onDateChange', newDate);
};
const swipeToNextDay = () => {
const nextDay = new Date(selectedDate);
nextDay.setDate(selectedDate.getDate() + 1);
setSelectedDate(nextDay);
setCurrentDate(nextDay);
DeviceEventEmitter.emit('onDateChange', nextDay);
};
const swipeToPrevDay = () => {
const prevDay = new Date(selectedDate);
prevDay.setDate(selectedDate.getDate() - 1);
setSelectedDate(prevDay);
setCurrentDate(prevDay);
DeviceEventEmitter.emit('onDateChange', prevDay);
};
const toggleMonthPicker = () => {
setShowMonthPicker(!showMonthPicker);
};
const panResponder = PanResponder.create({
onMoveShouldSetPanResponder: (evt, gestureState) => {
return Math.abs(gestureState.dx) > 30 && Math.abs(gestureState.dy) < 100;
},
onPanResponderMove: (evt, gestureState) => {},
onPanResponderRelease: (evt, gestureState) => {
if (gestureState.dx > 50) {
swipeToPrevDay();
} else if (gestureState.dx < -50) {
swipeToNextDay();
}
},
});
const calculateEventPosition = (startTime, endTime) => {
const [startHour, startMinute] = startTime.split(':').map(Number);
const [endHour, endMinute] = endTime.split(':').map(Number);
const startTotalMinutes = startHour * 60 + startMinute;
const endTotalMinutes = endHour * 60 + endMinute;
const durationMinutes = endTotalMinutes - startTotalMinutes;
const HOUR_HEIGHT = 80;
const topPosition = (startTotalMinutes / 60) * HOUR_HEIGHT;
const height = (durationMinutes / 60) * HOUR_HEIGHT;
return { topPosition, height };
};
return (
<FilterDateView
navigation={navigation}
currentDate={currentDate}
selectedDate={selectedDate}
showMonthPicker={showMonthPicker}
scrollViewRef={scrollViewRef}
panResponder={panResponder}
getEventsForDate={getEventsForDate}
getDayName={getDayName}
getMonthName={getMonthName}
handleMonthSelect={handleMonthSelect}
toggleMonthPicker={toggleMonthPicker}
calculateEventPosition={calculateEventPosition}
/>
);
};
export default FilterDate;
\ No newline at end of file
import React from 'react';
import {
View,
Text,
TouchableOpacity,
ScrollView,
StyleSheet,
Dimensions,
SafeAreaView,
} from 'react-native';
import R from '../../../assets/R';
const {width: screenWidth, height: screenHeight} = Dimensions.get('window');
const HOUR_HEIGHT = 80;
const FilterDateView= ({
navigation,
currentDate,
selectedDate,
showMonthPicker,
scrollViewRef,
panResponder,
getEventsForDate,
getDayName,
getMonthName,
handleMonthSelect,
toggleMonthPicker,
calculateEventPosition,
}) => {
const renderMonthPicker = () => {
if (!showMonthPicker) return null;
return (
<View style={styles.monthPickerContainer}>
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
contentContainerStyle={styles.monthPickerContent}>
{Array.from({length: 12}, (_, index) => (
<TouchableOpacity
key={index}
style={[
styles.monthItem,
currentDate.getMonth() === index && styles.monthItemSelected,
]}
onPress={() => handleMonthSelect(index)}>
<Text
style={[
styles.monthItemText,
currentDate.getMonth() === index && styles.monthItemTextSelected,
]}>
{getMonthName(index)}
</Text>
</TouchableOpacity>
))}
</ScrollView>
</View>
);
};
const renderDateInfo = () => {
return (
<View style={{backgroundColor: R.colors.grey_200}}>
<View style={styles.dateInfoContainer}>
<Text style={styles.dayName}>{getDayName(selectedDate)}</Text>
<Text style={styles.dayNumber}>{selectedDate.getDate()}</Text>
</View>
</View>
);
};
const renderTimeSlots = () => {
const hours = Array.from({length: 24}, (_, i) => i);
const selectedEvents = getEventsForDate(selectedDate);
return (
<View style={styles.timeSlotsContainer} {...panResponder.panHandlers}>
<ScrollView
ref={scrollViewRef}
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.scrollContent}>
<View style={styles.timelineContainer}>
<View style={styles.timeLabelsColumn}>
{hours.map((hour) => {
const timeStr = hour.toString().padStart(2, '0') + ':00';
return (
<View key={hour} style={styles.timeSlot}>
<Text style={styles.timeText}>{timeStr}</Text>
</View>
);
})}
</View>
<View style={styles.eventsColumn}>
{hours.map((hour) => (
<View key={hour} style={styles.gridLine} />
))}
{selectedEvents.map((event) => {
const { topPosition, height } = calculateEventPosition(event.time, event.endTime);
return (
<TouchableOpacity
key={event.id}
style={[
styles.eventCard,
{
position: 'absolute',
top: topPosition,
height: Math.max(height, 40),
left: 5,
right: 15,
zIndex: 10,
backgroundColor: R.colors.main,
}
]}
activeOpacity={0.7}>
<Text style={styles.eventTitle} numberOfLines={height > 60 ? 2 : 1}>
{event.title}
</Text>
{height > 40 && (
<Text style={styles.eventSubtitle} numberOfLines={1}>
{event.subtitle}
</Text>
)}
<Text style={styles.eventTime}>
{event.time} - {event.endTime}
</Text>
</TouchableOpacity>
);
})}
</View>
</View>
</ScrollView>
</View>
);
};
return (
<SafeAreaView style={styles.container}>
{renderMonthPicker()}
{renderDateInfo()}
{renderTimeSlots()}
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: R.colors.white,
},
monthPickerContainer: {
backgroundColor: R.colors.white,
borderBottomWidth: 1,
borderBottomColor: R.colors.grey_200,
paddingVertical: 10,
},
monthPickerContent: {
paddingHorizontal: 15,
},
monthItem: {
paddingHorizontal: 20,
paddingVertical: 8,
marginRight: 10,
borderRadius: 20,
backgroundColor: R.colors.grey_50,
},
dateInfoContainer: {
paddingHorizontal: 15,
paddingVertical: 12,
alignItems: 'center',
justifyContent:'center',
maxWidth:70,
borderRightWidth:1,
borderRightColor:R.colors.grey_200,
},
dayName: {
fontSize: R.fontsize.fontsSize12,
fontFamily: R.fonts.InterRegular,
color: R.colors.black,
marginBottom: 2,
},
dayNumber: {
fontSize: R.fontsize.fontsSize14,
fontFamily: R.fonts.InterSemiBold,
color: R.colors.blue500,
},
timeSlotsContainer: {
flex: 1,
backgroundColor: R.colors.white,
},
scrollContent: {
paddingBottom: 50,
},
timelineContainer: {
flexDirection: 'row',
position: 'relative',
},
timeLabelsColumn: {
minWidth: 70,
borderRightWidth: 1,
borderRightColor: R.colors.grey_200,
},
eventsColumn: {
flex: 1,
position: 'relative',
minHeight: 24 * HOUR_HEIGHT,
},
timeSlot: {
height: HOUR_HEIGHT,
alignItems: 'center',
justifyContent: 'center',
borderBottomWidth: 1,
borderBottomColor: R.colors.grey_100,
},
gridLine: {
height: HOUR_HEIGHT,
borderBottomWidth: 1,
borderBottomColor: R.colors.grey_100,
width: '100%',
},
timeText: {
fontSize: R.fontsize.fontsSize12,
fontFamily: R.fonts.InterRegular,
color: R.colors.black,
},
eventCard: {
borderRadius: 15,
paddingLeft: 15,
paddingTop: 10,
},
eventTitle: {
fontSize: R.fontsize.fontsSize12,
fontFamily: R.fonts.InterRegular,
color: R.colors.white,
fontWeight: '400',
marginBottom: 5,
},
eventSubtitle: {
fontSize: R.fontsize.fontsSize10,
fontFamily: R.fonts.InterRegular,
fontWeight: '400',
color: R.colors.white,
marginBottom: 5,
},
eventTime: {
fontSize: R.fontsize.fontsSize10,
fontFamily: R.fonts.InterRegular,
color: R.colors.white,
fontWeight: '400',
},
});
export default FilterDateView
\ No newline at end of file
import React from 'react';
import {Text, View, StyleSheet} from 'react-native';
import FilterWeekView from './view';
const FilterWeek = (props) => {
return (
<FilterWeekView />
);
};
export default FilterWeek;
import React, { useState, useRef, useEffect } from 'react';
import {
View,
Text,
TouchableOpacity,
ScrollView,
StyleSheet,
Dimensions,
SafeAreaView,
DeviceEventEmitter,
PanResponder,
} from 'react-native';
import R from '../../../assets/R';
const { width: screenWidth, height: screenHeight } = Dimensions.get('window');
const HOUR_HEIGHT = 80;
const DAY_COLUMN_WIDTH = (screenWidth - 70) / 7;
const FilterWeekView = ({ navigation }) => {
const [currentDate, setCurrentDate] = useState(new Date(2025, 6, 24));
const [selectedDate, setSelectedDate] = useState(new Date(2025, 6, 24));
const [showMonthPicker, setShowMonthPicker] = useState(false);
const scrollViewRef = useRef(null);
useEffect(() => {
DeviceEventEmitter.emit('onDateChange', selectedDate);
}, [selectedDate]);
const createMockEvents = () => {
return [
{
id: '1',
title: 'Lịch vào trực lớp TTCĐT 445.T1',
subtitle: 'CS Địa lý 4D',
time: '07:00',
endTime: '09:00',
date: '2025-07-21',
type: 'class',
},
{
id: '2',
title: 'Meeting team development',
subtitle: 'Phòng họp A1',
time: '10:00',
endTime: '11:30',
date: '2025-07-22',
type: 'meeting',
},
{
id: '3',
title: 'Training React Native',
subtitle: 'Online Zoom',
time: '14:00',
endTime: '16:00',
date: '2025-07-23',
type: 'training',
},
{
id: '4',
title: 'Code Review Session',
subtitle: 'Dev Team',
time: '09:00',
endTime: '10:30',
date: '2025-07-24',
type: 'review',
},
{
id: '5',
title: 'Sprint Planning',
subtitle: 'Scrum Team',
time: '15:00',
endTime: '17:00',
date: '2025-07-25',
type: 'meeting',
},
{
id: '6',
title: 'Tech Talk',
subtitle: 'Conference Room',
time: '11:00',
endTime: '12:00',
date: '2025-07-26',
type: 'training',
},
];
};
const mockEvents = createMockEvents();
const formatDateToString = (date) => {
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
return `${year}-${month}-${day}`;
};
const getEventsForDate = (date) => {
const dateStr = formatDateToString(date);
return mockEvents.filter(event => event.date === dateStr);
};
const getDayName = (date) => {
const days = ['CN', 'T2', 'T3', 'T4', 'T5', 'T6', 'T7'];
return days[date.getDay()];
};
const getMonthName = (monthIndex) => {
const months = [
'Tháng 1', 'Tháng 2', 'Tháng 3', 'Tháng 4', 'Tháng 5', 'Tháng 6',
'Tháng 7', 'Tháng 8', 'Tháng 9', 'Tháng 10', 'Tháng 11', 'Tháng 12',
];
return months[monthIndex];
};
const getWeekDates = (date) => {
const startOfWeek = new Date(date);
startOfWeek.setDate(date.getDate() - date.getDay());
const weekDates = [];
for (let i = 0; i < 7; i++) {
const weekDate = new Date(startOfWeek);
weekDate.setDate(startOfWeek.getDate() + i);
weekDates.push(weekDate);
}
return weekDates;
};
const handleMonthSelect = (monthIndex) => {
const newDate = new Date(currentDate);
newDate.setMonth(monthIndex);
setCurrentDate(newDate);
setSelectedDate(newDate);
setShowMonthPicker(false);
DeviceEventEmitter.emit('onDateChange', newDate);
};
const swipeToNextWeek = () => {
const nextWeek = new Date(currentDate);
nextWeek.setDate(currentDate.getDate() + 7);
setCurrentDate(nextWeek);
setSelectedDate(nextWeek);
DeviceEventEmitter.emit('onDateChange', nextWeek);
};
const swipeToPrevWeek = () => {
const prevWeek = new Date(currentDate);
prevWeek.setDate(currentDate.getDate() - 7);
setCurrentDate(prevWeek);
setSelectedDate(prevWeek);
DeviceEventEmitter.emit('onDateChange', prevWeek);
};
const toggleMonthPicker = () => {
setShowMonthPicker(!showMonthPicker);
};
const panResponder = PanResponder.create({
onMoveShouldSetPanResponder: (evt, gestureState) => {
return Math.abs(gestureState.dx) > 30 && Math.abs(gestureState.dy) < 100;
},
onPanResponderMove: (evt, gestureState) => { },
onPanResponderRelease: (evt, gestureState) => {
if (gestureState.dx > 50) {
swipeToPrevWeek();
} else if (gestureState.dx < -50) {
swipeToNextWeek();
}
},
});
const calculateEventPosition = (startTime, endTime) => {
const [startHour, startMinute] = startTime.split(':').map(Number);
const [endHour, endMinute] = endTime.split(':').map(Number);
const startTotalMinutes = startHour * 60 + startMinute;
const endTotalMinutes = endHour * 60 + endMinute;
const durationMinutes = endTotalMinutes - startTotalMinutes;
const topPosition = (startTotalMinutes / 60) * HOUR_HEIGHT;
const height = (durationMinutes / 60) * HOUR_HEIGHT;
return { topPosition, height };
};
const renderMonthPicker = () => {
if (!showMonthPicker) return null;
return (
<View style={styles.monthPickerContainer}>
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
contentContainerStyle={styles.monthPickerContent}>
{Array.from({ length: 12 }, (_, index) => (
<TouchableOpacity
key={index}
style={[
styles.monthItem,
currentDate.getMonth() === index && styles.monthItemSelected,
]}
onPress={() => handleMonthSelect(index)}>
<Text
style={[
styles.monthItemText,
currentDate.getMonth() === index && styles.monthItemTextSelected,
]}>
{getMonthName(index)}
</Text>
</TouchableOpacity>
))}
</ScrollView>
</View>
);
};
const renderWeekHeader = () => {
const weekDates = getWeekDates(currentDate);
return (
<View style={styles.weekHeaderContainer}>
<View style={styles.timeColumnHeader} />
{weekDates.map((date, index) => (
<View key={index} style={styles.dayHeaderCell}>
<Text style={styles.dayHeaderText}>{getDayName(date)}</Text>
<Text style={styles.dayHeaderNumber}>{date.getDate()}</Text>
</View>
))}
</View>
);
};
const renderTimeSlots = () => {
const hours = Array.from({ length: 24 }, (_, i) => i);
const weekDates = getWeekDates(currentDate);
return (
<View style={styles.timeSlotsContainer} {...panResponder.panHandlers}>
<ScrollView
ref={scrollViewRef}
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.scrollContent}>
<View style={styles.timelineContainer}>
<View style={styles.timeLabelsColumn}>
{hours.map((hour) => {
const timeStr = hour.toString().padStart(2, '0') + ':00';
return (
<View key={hour} style={styles.timeSlot}>
<Text style={styles.timeText}>{timeStr}</Text>
</View>
);
})}
</View>
<View style={styles.weekGridContainer}>
{weekDates.map((date, dayIndex) => (
<View key={dayIndex} style={styles.dayColumn}>
{hours.map((hour) => (
<View key={hour} style={styles.gridCell} />
))}
{getEventsForDate(date).map((event) => {
const { topPosition, height } = calculateEventPosition(event.time, event.endTime);
return (
<TouchableOpacity
key={event.id}
style={[
styles.eventCard,
{
position: 'absolute',
top: topPosition,
height: Math.max(height, 40),
left: 2,
right: 2,
zIndex: 10,
backgroundColor: R.colors.blue500,
}
]}
activeOpacity={0.7}>
<Text style={styles.eventTitle} numberOfLines={height > 60 ? 2 : 1}>
{event.title}
</Text>
{height > 40 && (
<Text style={styles.eventSubtitle} numberOfLines={1}>
{event.subtitle}
</Text>
)}
<Text style={styles.eventTime}>
{event.time} - {event.endTime}
</Text>
</TouchableOpacity>
);
})}
</View>
))}
</View>
</View>
</ScrollView>
</View>
);
};
return (
<SafeAreaView style={styles.container}>
{renderMonthPicker()}
{renderWeekHeader()}
{renderTimeSlots()}
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: R.colors.white,
},
monthPickerContainer: {
backgroundColor: R.colors.white,
borderBottomWidth: 1,
borderBottomColor: R.colors.grey_200,
paddingVertical: 10,
},
monthPickerContent: {
paddingHorizontal: 15,
},
monthItem: {
paddingHorizontal: 20,
paddingVertical: 8,
marginRight: 10,
borderRadius: 20,
backgroundColor: R.colors.grey_50,
},
monthItemSelected: {
backgroundColor: R.colors.blue500,
},
monthItemText: {
fontSize: R.fontsize.fontsSize12,
fontFamily: R.fonts.InterRegular,
color: R.colors.black,
},
monthItemTextSelected: {
color: R.colors.white,
fontFamily: R.fonts.InterMedium,
},
weekHeaderContainer: {
flexDirection: 'row',
backgroundColor: R.colors.grey_200,
borderBottomWidth: 1,
borderBottomColor: R.colors.grey_200,
},
timeColumnHeader: {
width: 70,
},
dayHeaderCell: {
width: DAY_COLUMN_WIDTH,
alignItems: 'center',
justifyContent: 'center',
borderRightWidth: 1,
borderRightColor: R.colors.grey_200,
},
dayHeaderText: {
fontSize: R.fontsize.fontsSize10,
fontFamily: R.fonts.InterRegular,
color: R.colors.grey_600,
},
dayHeaderNumber: {
fontSize: R.fontsize.fontsSize12,
fontFamily: R.fonts.InterMedium,
color: R.colors.black,
},
timeSlotsContainer: {
flex: 1,
backgroundColor: R.colors.white,
},
timelineContainer: {
flexDirection: 'row',
position: 'relative',
},
timeLabelsColumn: {
width: 70,
borderRightWidth: 1,
borderRightColor: R.colors.grey_200,
},
weekGridContainer: {
flex: 1,
flexDirection: 'row',
},
dayColumn: {
width: DAY_COLUMN_WIDTH,
position: 'relative',
borderRightWidth: 1,
borderRightColor: R.colors.grey_200,
},
timeSlot: {
height: HOUR_HEIGHT,
alignItems: 'center',
justifyContent: 'center',
borderBottomWidth: 1,
borderBottomColor: R.colors.grey_200,
},
gridCell: {
height: HOUR_HEIGHT,
borderBottomWidth: 1,
borderBottomColor: R.colors.grey_200,
width: '100%',
},
timeText: {
fontSize: R.fontsize.fontSizeContent,
fontFamily: R.fonts.InterRegular,
color: R.colors.black,
},
eventCard: {
borderRadius: 10,
paddingHorizontal: 4,
paddingVertical: 2,
justifyContent: 'center',
},
eventTitle: {
fontSize: R.fontsize.fontsSize10,
fontFamily: R.fonts.InterMedium,
color: R.colors.white,
marginBottom: 1,
},
eventSubtitle: {
fontSize: R.fontsize.fontsSize8,
fontFamily: R.fonts.InterRegular,
color: R.colors.white,
},
eventTime: {
fontSize: R.fontsize.fontsSize8,
fontFamily: R.fonts.InterRegular,
color: R.colors.white,
},
});
export default FilterWeekView;
\ No newline at end of file
import React, {useState, useMemo, useRef} from 'react';
import {Animated, PanResponder, Dimensions} from 'react-native';
import ClassScheduleView from './view';
const {height: screenHeight} = Dimensions.get('window');
const BOTTOM_SHEET_HEIGHT = screenHeight * 0.6;
const ClassSchedule = ({events = [], onDateSelect, onEventPress}) => {
const [currentDate, setCurrentDate] = useState(new Date(2025, 7, 1));
const [selectedDate, setSelectedDate] = useState(null);
const [showBottomSheet, setShowBottomSheet] = useState(false);
const bottomSheetTranslateY = useRef(new Animated.Value(BOTTOM_SHEET_HEIGHT)).current;
const formatDateToString = (date) => {
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
return `${year}-${month}-${day}`;
};
const createMockEvents = () => {
const today = new Date();
const todayStr = formatDateToString(today);
const tomorrow = new Date(today);
tomorrow.setDate(tomorrow.getDate() + 1);
const tomorrowStr = formatDateToString(tomorrow);
return [
{
id: '1',
title: 'Meeting hôm nay',
date: todayStr,
time: '10:00',
endTime: '11:00',
description: 'Họp team development',
type: 'meeting',
},
{
id: '2',
title: 'Demo hôm nay',
date: todayStr,
time: '15:00',
endTime: '16:00',
description: 'Present tính năng mới',
type: 'demo',
},
{
id: '11',
title: 'Lịch học lớp IT47.8F7',
date: '2025-08-04',
time: '07:00',
endTime: '08:30',
description: 'Môn học chuyên ngành',
type: 'class',
},
{
id: '12',
title: 'Meeting team',
date: '2025-08-04',
time: '10:00',
endTime: '11:00',
description: 'Họp team development',
type: 'meeting',
},
{
id: '13',
title: 'Training React Native',
date: '2025-08-05',
time: '14:00',
endTime: '16:00',
description: 'Học New Architecture',
type: 'training',
},
{
id: '14',
title: 'Code Review',
date: '2025-08-05',
time: '10:30',
endTime: '11:30',
description: 'Review PR #123',
type: 'review',
},
{
id: '15',
title: 'Họp nội bộ giữa giảng viên bộ môn công nghệ phần mềm',
date: '2025-08-05',
time: '09:00',
endTime: '11:30',
description: 'Thảo luận chương trình đào tạo mới',
type: 'meeting',
},
{
id: '16',
title: 'Sự kiện thắp sáng ước mơ kỹ thuật',
date: '2025-08-05',
time: '13:00',
endTime: '15:30',
description: 'Chương trình định hướng nghề nghiệp cho sinh viên',
type: 'event',
},
{
id: '17',
title: 'Lịch học lớp EWC45.364.L1',
date: '2025-08-05',
time: '14:00',
endTime: '15:30',
description: 'Tiếng Anh chuyên ngành',
type: 'class',
},
{
id: '18',
title: 'Họp tổng kết quả chấm nghiệm cứu khoa học sinh viên khóa K18',
date: '2025-08-05',
time: '17:00',
endTime: '20:30',
description: 'Đánh giá kết quả nghiên cứu khoa học của sinh viên',
type: 'meeting',
},
{
id: '3',
title: 'Training React Native',
date: tomorrowStr,
time: '14:00',
endTime: '16:00',
description: 'Học New Architecture',
type: 'training',
},
{
id: '4',
title: 'Code Review',
date: tomorrowStr,
time: '10:30',
endTime: '11:30',
description: 'Review PR #123',
type: 'review',
},
{
id: '10',
title: 'Demo sản phẩm',
date: '2025-08-25',
time: '15:00',
endTime: '16:30',
description: 'Present tính năng mới',
type: 'demo',
},
];
};
const mockEvents = createMockEvents();
const allEvents = [...events, ...mockEvents];
const panResponder = useRef(
PanResponder.create({
onMoveShouldSetPanResponder: (evt, gestureState) => {
return Math.abs(gestureState.dy) > 10;
},
onPanResponderMove: (evt, gestureState) => {
if (gestureState.dy > 0) {
bottomSheetTranslateY.setValue(gestureState.dy);
}
},
onPanResponderRelease: (evt, gestureState) => {
if (gestureState.dy > 100) {
hideBottomSheetModal();
} else {
Animated.spring(bottomSheetTranslateY, {
toValue: 0,
useNativeDriver: true,
}).start();
}
},
})
).current;
const getMonthData = useMemo(() => {
const year = currentDate.getFullYear();
const month = currentDate.getMonth();
const firstDay = new Date(year, month, 1);
const lastDay = new Date(year, month + 1, 0);
const startDate = new Date(firstDay);
startDate.setDate(firstDay.getDate() - firstDay.getDay());
const days = [];
const currentDateObj = new Date(startDate);
for (let i = 0; i < 42; i++) {
days.push(new Date(currentDateObj));
currentDateObj.setDate(currentDateObj.getDate() + 1);
}
return {
year,
month,
firstDay,
lastDay,
days,
};
}, [currentDate]);
const getEventsForDate = (date) => {
const dateStr = formatDateToString(date);
return allEvents.filter(event => event.date === dateStr);
};
const parseLocalDate = (dateString) => {
const [year, month, day] = dateString.split('-').map(Number);
return new Date(year, month - 1, day);
};
const formatDateToDisplay = (date) => {
const day = date.getDate().toString().padStart(2, '0');
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const year = date.getFullYear();
return `Lịch ngày ${day}/${month}/${year}`;
};
const isCurrentMonth = (date) => {
return date.getMonth() === currentDate.getMonth();
};
const isToday = (date) => {
const today = new Date();
return (
date.getDate() === today.getDate() &&
date.getMonth() === today.getMonth() &&
date.getFullYear() === today.getFullYear()
);
};
const navigateMonth = (direction) => {
const newDate = new Date(currentDate);
if (direction === 'prev') {
newDate.setMonth(newDate.getMonth() - 1);
} else {
newDate.setMonth(newDate.getMonth() + 1);
}
setCurrentDate(newDate);
setSelectedDate(null);
if (showBottomSheet) {
hideBottomSheetModal();
}
};
const showBottomSheetModal = () => {
setShowBottomSheet(true);
Animated.spring(bottomSheetTranslateY, {
toValue: 0,
tension: 100,
friction: 8,
useNativeDriver: true,
}).start();
};
const hideBottomSheetModal = () => {
Animated.timing(bottomSheetTranslateY, {
toValue: BOTTOM_SHEET_HEIGHT,
duration: 300,
useNativeDriver: true,
}).start(() => {
setShowBottomSheet(false);
});
};
const handleDatePress = (date) => {
const dateStr = formatDateToString(date);
const dayEvents = getEventsForDate(date);
setSelectedDate(dateStr);
onDateSelect?.(dateStr);
if (dayEvents.length > 0) {
showBottomSheetModal();
}
};
const handleEventPress = (event) => {
onEventPress?.(event);
};
const handleCloseBottomSheet = () => {
hideBottomSheetModal();
};
const getSelectedEvents = () => {
if (!selectedDate) return [];
return allEvents
.filter(event => event.date === selectedDate)
.sort((a, b) => a.time.localeCompare(b.time));
};
return (
<ClassScheduleView
currentDate={currentDate}
selectedDate={selectedDate}
showBottomSheet={showBottomSheet}
bottomSheetTranslateY={bottomSheetTranslateY}
panResponder={panResponder}
getMonthData={getMonthData}
getEventsForDate={getEventsForDate}
parseLocalDate={parseLocalDate}
formatDateToDisplay={formatDateToDisplay}
isCurrentMonth={isCurrentMonth}
isToday={isToday}
navigateMonth={navigateMonth}
handleDatePress={handleDatePress}
handleEventPress={handleEventPress}
handleCloseBottomSheet={handleCloseBottomSheet}
getSelectedEvents={getSelectedEvents}
/>
);
};
export default ClassSchedule;
\ No newline at end of file
import {StyleSheet, Dimensions} from 'react-native';
import R from '../../assets/R';
const {width: screenWidth, height: screenHeight} = Dimensions.get('window');
const CELL_WIDTH = (screenWidth - 30) / 7;
const CELL_HEIGHT = (screenHeight - 140) / 6;
const BOTTOM_SHEET_HEIGHT = screenHeight * 0.6;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: R.colors.white,
alignItems: 'center',
},
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingVertical: 15,
},
header_title: {
fontSize: R.fontsize.fontsSize16,
fontFamily: R.fonts.InterMedium,
color: R.colors.black,
fontWeight: '600',
},
navButton: {
width: 30,
height: 30,
borderRadius: 20,
backgroundColor: R.colors.blue500,
alignItems: 'center',
justifyContent: 'center',
},
navButtonText: {
color: R.colors.white,
fontSize: R.fontsize.fontsSize16,
fontFamily: R.fonts.InterMedium,
},
weekDaysContainer: {
flexDirection: 'row',
paddingBottom: 5,
marginBottom: 5,
},
weekDayCell: {
width: CELL_WIDTH,
alignItems: 'center',
},
weekDayText: {
fontFamily: R.fonts.InterRegular,
fontSize: R.fontsize.fontsSize10,
fontWeight: '400',
color: R.colors.black,
},
calendarGrid: {
},
weekRow: {
flexDirection: 'row',
},
dayCell: {
width: CELL_WIDTH,
minHeight: CELL_HEIGHT,
borderWidth: 1,
borderColor: R.colors.grey_200,
padding: 4,
alignItems: 'center',
},
selectedDayCell: {
borderColor: R.colors.blue500,
borderWidth: 1,
},
dayText: {
fontSize: R.fontsize.fontsSize12,
fontWeight: '500',
fontFamily:R.fonts.InterMedium,
color: R.colors.black,
marginBottom: 2,
},
dayTextInactive: {
color: R.colors.grey_100,
opacity: 1,
},
selectedDayText: {
color: R.colors.black,
fontWeight: 'bold',
fontFamily: R.fonts.InterSemiBold,
},
todayText: {
color: R.colors.white,
fontWeight: 'bold',
fontFamily: R.fonts.InterSemiBold,
backgroundColor: R.colors.blue500,
borderRadius: 10,
paddingHorizontal: 5,
},
eventsContainer: {
width: '100%',
flex: 1,
},
eventBar: {
paddingVertical: 2,
paddingHorizontal: 5,
borderRadius: 5,
marginBottom: 2,
},
eventBarText: {
fontSize: R.fontsize.fontsSize10,
color: R.colors.white,
fontWeight: '500',
fontFamily: R.fonts.InterRegular
},
moreEventsText: {
fontSize: R.fontsize.fontsSize10,
color: R.colors.grey_100,
textAlign: 'center',
},
modalBackdrop: {
flex: 1,
backgroundColor: R.colors.grey_200,
justifyContent: 'flex-end',
},
bottomSheet: {
height: BOTTOM_SHEET_HEIGHT,
backgroundColor: R.colors.white,
borderTopLeftRadius: 20,
borderTopRightRadius: 20,
},
bottomSheetContent: {
paddingHorizontal: 15,
},
dragHandle: {
width: 40,
height: 4,
backgroundColor: R.colors.grey_200,
borderRadius: 2,
alignSelf: 'center',
marginTop: 10,
marginBottom: 15,
},
bottomSheetHeader: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
marginBottom: 20,
},
bottomSheetTitle: {
fontSize: R.fontsize.fontSizeHeader1,
fontFamily: R.fonts.InterMedium,
color: R.colors.black,
flex: 1,
},
closeButton: {
width: 30,
height: 30,
borderRadius: 15,
backgroundColor: R.colors.grey_200,
alignItems: 'center',
justifyContent: 'center',
},
closeButtonText: {
fontSize: R.fontsize.fontsSize12,
color: R.colors.grey_800,
fontFamily: R.fonts.InterRegular,
},
noEventsContainer: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
paddingVertical: 40,
},
noEventsText: {
fontSize: R.fontsize.fontsSize12,
fontFamily: R.fonts.InterRegular,
color: R.colors.grey_800,
fontWeight: '400'
},
eventCard: {
flexDirection: 'row',
backgroundColor: R.colors.white,
borderRadius: 12,
padding: 15,
marginBottom: 12,
borderLeftWidth: 4,
borderLeftColor: R.colors.blue500,
shadowColor: R.colors.black,
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 1,
shadowRadius: 1,
elevation: 2,
},
eventTimeContainer: {
minWidth: 80,
alignItems: 'flex-start',
justifyContent: 'flex-start',
marginRight: 15,
},
eventTime: {
fontSize: R.fontsize.fontsSize12,
fontFamily: R.fonts.InterMedium,
color: R.colors.blue500,
fontWeight: '600',
},
eventContent: {
flex: 1,
},
eventTitle: {
fontSize: R.fontsize.fontsSize12,
fontFamily: R.fonts.InterMedium,
color: R.colors.black,
fontWeight: '600',
marginBottom: 4,
},
eventDescription: {
fontSize: R.fontsize.fontsSize12,
fontFamily: R.fonts.InterRegular,
color: R.colors.grey_800,
},
});
export {styles, CELL_WIDTH, BOTTOM_SHEET_HEIGHT};
import React from 'react';
import {
Text,
View,
TouchableOpacity,
StyleSheet,
ScrollView,
Dimensions,
Modal,
Animated,
SafeAreaView,
} from 'react-native';
import R from '../../assets/R';
import { styles, CELL_WIDTH, BOTTOM_SHEET_HEIGHT } from './style';
import { useNavigation } from '@react-navigation/native';
import * as SCREENNAME from '../../routers/ScreenNames';
const ClassScheduleView = ({
currentDate,
selectedDate,
showBottomSheet,
bottomSheetTranslateY,
panResponder,
getMonthData,
getEventsForDate,
parseLocalDate,
formatDateToDisplay,
isCurrentMonth,
isToday,
navigateMonth,
handleDatePress,
handleEventPress,
handleCloseBottomSheet,
getSelectedEvents,
}) => {
const navigation = useNavigation();
const renderHeader = () => {
const monthNames = [
'Tháng 1', 'Tháng 2', 'Tháng 3', 'Tháng 4', 'Tháng 5', 'Tháng 6',
'Tháng 7', 'Tháng 8', 'Tháng 9', 'Tháng 10', 'Tháng 11', 'Tháng 12',
];
return (
<View style={styles.header}>
<TouchableOpacity
style={styles.navButton}
onPress={() => navigateMonth('prev')}>
<Text style={styles.navButtonText}></Text>
</TouchableOpacity>
<Text style={styles.header_title}>
{monthNames[getMonthData.month]} {getMonthData.year}
</Text>
<TouchableOpacity
style={styles.navButton}
onPress={() => navigateMonth('next')}>
<Text style={styles.navButtonText}></Text>
</TouchableOpacity>
</View>
);
};
const renderWeekDays = () => {
const weekDays = ['CN', 'T2', 'T3', 'T4', 'T5', 'T6', 'T7'];
return (
<View style={styles.weekDaysContainer}>
{weekDays.map((day, index) => (
<View key={index} style={styles.weekDayCell}>
<Text style={styles.weekDayText}>{day}</Text>
</View>
))}
</View>
);
};
const renderDayCell = (date, index) => {
const dayEvents = getEventsForDate(date);
const isSelected = selectedDate === formatDateToString(date);
const isTodayDate = isToday(date);
const isInCurrentMonth = isCurrentMonth(date);
return (
<TouchableOpacity
key={index}
style={[
styles.dayCell,
isSelected && styles.selectedDayCell,
isTodayDate && styles.todayCell,
]}
onPress={() => handleDatePress(date)}
activeOpacity={0.7}>
<Text
style={[
styles.dayText,
!isInCurrentMonth && styles.dayTextInactive,
isSelected && styles.selectedDayText,
isTodayDate && styles.todayText,
]}>
{date.getDate()}
</Text>
{dayEvents.length > 0 && (
<View style={styles.eventsContainer}>
{dayEvents.slice(0, 2).map((event, eventIndex) => (
<TouchableOpacity
key={event.id}
style={[
styles.eventBar,
{ backgroundColor: R.colors.main },
]}
onPress={() => handleEventPress(event)}>
<Text style={styles.eventBarText} numberOfLines={1}>
{event.title}
</Text>
</TouchableOpacity>
))}
{dayEvents.length > 2 && (
<Text style={styles.moreEventsText}>+{dayEvents.length - 2}</Text>
)}
</View>
)}
</TouchableOpacity>
);
};
const formatDateToString = (date) => {
const year = date.getFullYear();
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
return `${year}-${month}-${day}`;
};
const renderCalendarGrid = () => {
const weeks = [];
for (let i = 0; i < 6; i++) {
const week = getMonthData.days.slice(i * 7, (i + 1) * 7);
weeks.push(
<View key={i} style={styles.weekRow}>
{week.map((date, dayIndex) =>
renderDayCell(date, i * 7 + dayIndex),
)}
</View>,
);
}
return <View style={styles.calendarGrid}>{weeks}</View>;
};
const renderBottomSheetContent = () => {
if (!selectedDate) return null;
const selectedDateObj = parseLocalDate(selectedDate);
const selectedEvents = getSelectedEvents();
return (
<View style={styles.bottomSheetContent}>
<View style={styles.dragHandle} />
<View style={styles.bottomSheetHeader}>
<Text style={styles.bottomSheetTitle}>
{formatDateToDisplay(selectedDateObj)}
</Text>
<TouchableOpacity
style={styles.closeButton}
onPress={handleCloseBottomSheet}>
<Text style={styles.closeButtonText}></Text>
</TouchableOpacity>
</View>
<ScrollView
style={styles.eventsScrollView}
showsVerticalScrollIndicator={false}>
{selectedEvents.length === 0 ? (
<View style={styles.noEventsContainer}>
<Text style={styles.noEventsText}>Không có s kin nào</Text>
</View>
) : (
selectedEvents.map((event, index) => (
<TouchableOpacity
key={event.id}
style={styles.eventCard}
onPress={() => navigation.navigate(SCREENNAME.DETAILCLASSSCHEDULE, { event })}
activeOpacity={0.7}>
<View style={styles.eventTimeContainer}>
<Text style={styles.eventTime}>
{event.time}
{event.endTime && ` - ${event.endTime}`}
</Text>
</View>
<View style={styles.eventContent}>
<Text style={styles.eventTitle} numberOfLines={2}>
{event.title}
</Text>
{event.description && (
<Text style={styles.eventDescription} numberOfLines={3}>
{event.description}
</Text>
)}
</View>
</TouchableOpacity>
))
)}
</ScrollView>
</View>
);
};
const renderBottomSheet = () => {
return (
<Modal
visible={showBottomSheet}
transparent={true}
animationType="none"
onRequestClose={handleCloseBottomSheet}>
<TouchableOpacity
style={styles.modalBackdrop}
activeOpacity={1}
onPress={handleCloseBottomSheet}>
<Animated.View
style={[
styles.bottomSheet,
{
transform: [{ translateY: bottomSheetTranslateY }],
},
]}
{...panResponder.panHandlers}>
<TouchableOpacity activeOpacity={1}>
{renderBottomSheetContent()}
</TouchableOpacity>
</Animated.View>
</TouchableOpacity>
</Modal>
);
};
return (
<SafeAreaView style={styles.container}>
<ScrollView showsVerticalScrollIndicator={false}>
{renderHeader()}
{renderWeekDays()}
{renderCalendarGrid()}
</ScrollView>
{renderBottomSheet()}
</SafeAreaView>
);
};
export default ClassScheduleView;
\ No newline at end of file
import React, { useState, useMemo } from "react";
import {
Text,
View,
StyleSheet,
Modal,
Pressable,
Image,
Touchable,
TouchableOpacity,
} from "react-native";
import DebtView from "./view";
import R from "../../assets/R";
const Debt = (props) => {
const [activeTab, setActiveTab] = useState("WFP");
const IconImage = R.images.icImage;
const [dataListStatus, setDataListStatus] = useState([
{ key: "WFP", title_status: "Chờ thanh toán" },
{ key: "P", title_status: "Đã thanh toán" },
{ key: "O", title_status: "Quá hạn" },
]);
const handleTabChange = (tabKey) => {
setActiveTab(tabKey);
};
const closeModal = () => {
setModalVisible(false);
};
const [dataListDebtItem, setDataListDebtItem] = useState([
{
id: 1,
title_notifi_student_code:
"Trường thông báo sinh viên 086592 cần thanh toán khoản công nợ thư viện",
price: 50000,
code_debt: "hfhfkYRBFI37457HDNNF",
type_debt: "Đã thanh toán",
message_debt: "Em cần thanh toán nợ từ việc làm mất sách của trường",
payment_term: "23/07/2025",
status: "P",
},
{
id: 2,
title_notifi_student_code:
"Trường thông báo sinh viên 086592 cần thanh toán khoản công nợ thư viện",
price: 50000,
code_debt: "hfhfkYRBFI37457HDNNF",
type_debt: "Nợ thư viện",
message_debt: "Em cần thanh toán nợ từ việc làm mất sách của trường",
payment_term: "23/07/2025",
status: "O",
},
{
id: 3,
title_notifi_student_code:
"Trường thông báo sinh viên 086592 cần thanh toán khoản công nợ thư viện",
price: 50000,
code_debt: "hfhfkYRBFI37457HDNNF",
type_debt: "Chờ thanh toán",
message_debt: "Em cần thanh toán nợ từ việc làm mất sách của trường",
payment_term: "23/07/2025",
status: "WFP",
},
{
id: 4,
title_notifi_student_code:
"Trường thông báo sinh viên 086592 cần thanh toán khoản công nợ thư viện",
price: 50000,
code_debt: "hfhfkYRBFI37457HDNNF",
type_debt: "Chờ thanh toán",
message_debt: "Em cần thanh toán nợ từ việc làm mất sách của trường",
payment_term: "23/07/2025",
status: "WFP",
},
{
id: 5,
title_notifi_student_code:
"Trường thông báo sinh viên 086592 cần thanh toán khoản công nợ thư viện",
price: 50000,
code_debt: "hfhfkYRBFI37457HDNNF",
type_debt: "Chờ thanh toán",
message_debt: "Em cần thanh toán nợ từ việc làm mất sách của trường",
payment_term: "23/07/2025",
status: "WFP",
},
{
id: 6,
title_notifi_student_code:
"Trường thông báo sinh viên 086592 cần thanh toán khoản công nợ thư viện",
price: 50000,
code_debt: "hfhfkYRBFI37457HDNNF",
type_debt: "Chờ thanh toán",
message_debt: "Em cần thanh toán nợ từ việc làm mất sách của trường",
payment_term: "23/07/2025",
status: "WFP",
},
]);
const [timeDebt, setTimeDebt] = useState("Hôm nay, 21/07/2025");
const getItemDisplayConfig = (status, type_debt) => {
switch (status) {
case "WFP": // Chờ thanh toán
return {
titleColor:
type_debt === "Nợ thư viện" ? R.colors.red : R.colors.black,
priceColor: R.colors.textSubMain,
timeColor: R.colors.red,
typeDebtColor: R.colors.black,
showButton: true,
showPaymentTerm: true,
};
case "P":
return {
titleColor: R.colors.black,
priceColor: R.colors.green,
timeColor: false,
typeDebtColor: R.colors.black,
showButton: false,
};
case "O": // Quá hạn
return {
titleColor: R.colors.red,
priceColor: R.colors.textSubMain,
timeColor: R.colors.red,
typeDebtColor: R.colors.black,
showButton: true,
showPaymentTerm: true,
};
}
};
const handlePayPress = (item) => {
setModalVisible(true);
console.log("Đã bấm Thanh toán:", item);
};
const [modalVisible, setModalVisible] = useState(false);
const processedDebtItems = useMemo(() => {
return dataListDebtItem.map((item) => {
const displayConfig = getItemDisplayConfig(item.status, item.type_debt);
return {
id: item.id,
title_notifi_student_code: item.title_notifi_student_code,
price: item.price,
code_debt: item.code_debt,
type_debt: item.type_debt,
message_debt: item.message_debt,
payment_term: item.payment_term,
status: item.status,
displayConfig: displayConfig,
};
});
}, [dataListDebtItem]);
return (
<>
<DebtView
onTabChange={handleTabChange}
activeTab={activeTab}
dataListStatus={dataListStatus}
dataListDebtItem={processedDebtItems}
timeDebt={timeDebt}
onPayPress={handlePayPress}
/>
<Modal
visible={modalVisible}
transparent
animationType="slide"
onRequestClose={() => setModalVisible(false)}
>
<Pressable
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
backgroundColor: "rgba(0, 0, 0, 0.55)",
}}
onPress={closeModal}
>
<View
style={{
padding: 20,
backgroundColor: "white",
borderRadius: 10,
}}
>
<Image
source={R.images.igQrCode}
style={{
width: 280,
height: 380,
resizeMode: "contain",
}}
/>
<TouchableOpacity
style={{ alignItems: "center", justifyContent: "center" }}
onPress={console.log("Nhấn để lưu ảnh")}
>
<IconImage></IconImage>
<Text
style={{
fontFamily: R.fonts.InterRegular,
fontWeight: "300",
fontSize: R.fontsize.fontSizeContent,
color: R.colors.txtMain,
}}
>
Lưu nh
</Text>
</TouchableOpacity>
</View>
</Pressable>
</Modal>
</>
);
};
export default Debt;
import { StyleSheet, Text, View } from "react-native";
import React from "react";
import { useNavigation } from "@react-navigation/native";
import R from "../../assets/R";
import Button from "../../components/Button";
const ItemList = ({ item , onPayPress}) => {
const navigate = useNavigation();
const QrCodeIconButton = R.images.icQrCodeButton;
const { displayConfig } = item;
return (
<View style={styles.container}>
<Text style={[styles.title, { color: displayConfig.titleColor }]}>
{item.title_notifi_student_code}
</Text>
<View style={styles.container_content}>
<Text style={styles.sub_title}>S tin: </Text>
<Text style={[styles.price_text, { color: displayConfig.priceColor }]}>
{item.price}
</Text>
</View>
<View style={styles.container_content}>
<Text style={styles.sub_title}>Mã công n: </Text>
<Text style={styles.code}>{item.code_debt}</Text>
</View>
<View style={styles.container_content}>
<Text style={styles.sub_title}>Loi công n: </Text>
<Text style={[styles.type_of_debt, { color: displayConfig.typeDebtColor }]}>
{item.type_debt}
</Text>
</View>
<View style={styles.container_content}>
<Text style={styles.sub_title}>Ghi chú: </Text>
<Text style={[{ flex: 1 }, styles.note]}>{item.message_debt}</Text>
</View>
<View style={styles.container_content}>
{displayConfig.showPaymentTerm && (
<View style={{flex:1, flexDirection:'row'}}>
<Text style={styles.sub_title}>Hn thanh toán: </Text>
<Text style={[styles.time, { color: displayConfig.timeColor }]}>
{item.payment_term}
</Text>
</View>
)}
{displayConfig.showButton && (
<View style={styles.container_button}>
<Button
marginRight={0}
title="Thanh toán"
textStyle={styles.text_button}
icon={(props) => <QrCodeIconButton width={16} height={16} />}
backgroundColor={R.colors.buttonColorSel}
textColor={R.colors.white}
justifyContent="center"
alignItems="center"
height={32}
borderRadius={8}
paddingHorizontal={12}
iconSpacingHorizontal={4}
onPress={() => onPayPress?.(item)}
/>
</View>
)}
</View>
</View>
);
};
export default ItemList;
const styles = StyleSheet.create({
container: {
backgroundColor: R.colors.backgroundCard,
marginHorizontal: 15,
marginVertical: 5,
borderRadius: 10,
paddingVertical: 9,
paddingHorizontal: 15,
},
title: {
color: R.colors.black,
fontSize: R.fontsize.fontsSize14,
fontFamily: R.fonts.InterMedium,
fontWeight:'500',
lineHeight: 24,
},
sub_title: {
color: R.colors.black,
fontSize: R.fontsize.fontsSize12,
fontWeight: "300",
lineHeight: 24,
fontFamily: R.fonts.InterRegular,
},
price_text: {
color: R.colors.blue500,
fontFamily: R.fonts.InterRegular,
fontWeight: "bold",
lineHeight: 24,
fontSize: R.fontsize.fontsSize12,
},
code: {
color: R.colors.black,
fontFamily: R.fonts.InterRegular,
fontWeight: "bold",
lineHeight: 24,
fontSize: R.fontsize.fontsSize12,
},
type_of_debt: {
color: R.colors.black,
fontFamily: R.fonts.InterRegular,
fontWeight: "bold",
lineHeight: 24,
fontSize: R.fontsize.fontsSize12,
},
note: {
color: R.colors.black,
fontFamily: R.fonts.InterRegular,
fontWeight: "400",
lineHeight: 24,
fontSize: R.fontsize.fontsSize12,
},
time: {
color: R.colors.red,
fontFamily: R.fonts.InterRegular,
fontWeight: "400",
lineHeight: 24,
fontSize: R.fontsize.fontsSize12,
},
container_content: {
flexDirection: "row",
},
container_button:{
},
text_button:{
fontFamily: R.fonts.InterRegular,
fontWeight: "400",
fontSize: R.fontsize.fontsSize12,
},
});
import React, { useState } from "react";
import {
Text,
SafeAreaView,
TouchableOpacity,
FlatList,
StyleSheet,
Modal,
View,
Image,
TextInput,
} from "react-native";
import Header from "../../components/Header/Header";
import I18n from "../../helper/i18/i18n";
import R from "../../assets/R";
import ItemList from "./item";
const DebtView = (props) => {
const { onTabChange, dataListStatus, dataListDebtItem, activeTab, timeDebt } =
props;
const IconSearch = R.images.icSearch
const renderTabViewItem = ({ item }) => {
const isActive = activeTab === item.key;
return (
<TouchableOpacity
style={[styles.tab_button, isActive && styles.tabButtonActive]}
onPress={() => onTabChange(item.key)}
>
<Text
style={[styles.tabButtonText, isActive && styles.tabButtonTextActive]}
>
{item.title_status}
</Text>
</TouchableOpacity>
);
};
const renderItem = ({ item }) => {
return <ItemList item={item} onPayPress={props.onPayPress} />;
};
const filteredDebtItems = dataListDebtItem.filter(
(item) => item.status === activeTab
);
return (
<SafeAreaView style={styles.container}>
<Header isBack title={I18n.t("Debt")} />
<View style={styles.tab_container}>
<FlatList
data={dataListStatus}
renderItem={renderTabViewItem}
keyExtractor={(item) => item.key}
horizontal
showsHorizontalScrollIndicator={false}
contentContainerStyle={styles.tab_scroll_content}
/>
</View>
<View style={{ marginHorizontal: 15, flexDirection: "row", alignItems: "center", borderWidth: 1, borderColor: R.colors.gray400, borderRadius: 50, padding: 0, maxHeight: 40, paddingHorizontal: 10, marginVertical: 5 }}>
<IconSearch width={20} height={20} stroke={R.colors.gray400}/>
<TextInput
placeholder={"Tìm kiếm"}
style={styles.input}
placeholderTextColor={R.colors.gray400}
/>
</View>
<Text style={styles.sub_text}>{props.timeDebt}</Text>
<FlatList
data={filteredDebtItems}
renderItem={renderItem}
showsVerticalScrollIndicator={false}
keyExtractor={(item) => item.id.toString()}
/>
</SafeAreaView>
);
};
export default DebtView;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: R.colors.white,
},
input: {
height: 40,
paddingHorizontal: 10,
fontFamily: R.fonts.InterRegular,
fontWeight: "400",
color: R.colors.gray400,
fontSize: R.fontsize.fontsSize12,
},
tab_container: {
marginHorizontal: 10,
justifyContent: "center",
},
tab_button: {
marginHorizontal: 5,
borderRadius: 15,
width:132,
backgroundColor: R.colors.gray400,
alignItems: "center",
justifyContent:"center"
},
tabButtonActive: {
backgroundColor: R.colors.blue500,
},
tabButtonText: {
fontFamily: R.fonts.InterSemiBold,
fontWeight: "600",
fontSize: R.fontsize.fontsSize12,
color: R.colors.white,
},
tabButtonTextActive: {
color: R.colors.white,
fontWeight: "600",
fontFamily: R.fonts.InterSemiBold,
fontSize: R.fontsize.fontsSize12,
},
tab_scroll_content: {
height: 28,
marginTop:5
},
sub_text: {
color: R.colors.black,
marginLeft: 15,
fontFamily: R.fonts.InterRegular,
fontWeight: "300",
fontSize: R.fontsize.fontsSize12,
},
});
import React from 'react';
import {Text, View} from 'react-native';
import ELearningView from './view';
const Elearning = ({props}) => <ELearningView />;
export default Elearning;
import React from 'react';
import {Text, View, TouchableOpacity} from 'react-native';
const ELearningView = ({props}) => (
<View
style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}}>
<TouchableOpacity>
<Text>ELearning</Text>
</TouchableOpacity>
</View>
);
export default ELearningView;
import React, { useState } from 'react';
import {Text, View, StyleSheet} from 'react-native';
import ExamScheduleView from './view';
const ExamSchedule = (props) => {
const [dataExamSchedule, setDataExamSchedule] = useState([
{
id: 1,
code_exam: 'MD001',
code_class:'MD0081',
poetry:'123 (7:30 - 10:30)',
time_exam:'2025-08-05',
class_exam:'A98',
day_exam:'Thứ 3',
description: 'Bảo vệ/ DA',
},
{
id: 2,
code_exam: 'MD001',
code_class:'MD0081',
poetry:'123 (7:30 - 10:30)',
time_exam:'2025-08-05',
class_exam:'A98',
day_exam:'Thứ 3',
description: 'Bảo vệ/ DA',
},
{
id: 3,
code_exam: 'MD001',
code_class:'MD0081',
poetry:'123 (7:30 - 10:30)',
time_exam:'2025-08-05',
class_exam:'A98',
day_exam:'Thứ 3',
description: 'Bảo vệ/ DA',
},
{
id: 4,
code_exam: 'MD001',
code_class:'MD0081',
poetry:'123 (7:30 - 10:30)',
time_exam:'2025-08-05',
class_exam:'A98',
day_exam:'Thứ 3',
description: 'Bảo vệ/ DA',
},
{
id: 5,
code_exam: 'MD001',
code_class:'MD0081',
poetry:'123 (7:30 - 10:30)',
time_exam:'2025-08-05',
class_exam:'A98',
day_exam:'Thứ 3',
description: 'Bảo vệ/ DA',
},
{
id: 6,
code_exam: 'MD001',
code_class:'MD0081',
poetry:'123 (7:30 - 10:30)',
time_exam:'2025-08-05',
class_exam:'A98',
day_exam:'Thứ 3',
description: 'Bảo vệ/ DA',
},
]);
return (
<ExamScheduleView dataExamSchedule={dataExamSchedule} />
);
};
export default ExamSchedule;
import R from "../../assets/R";
import { Text, View, TouchableOpacity, StyleSheet, Image } from 'react-native';
const ItemView = ({item}) => {
return (
<TouchableOpacity style={styles.container}>
<Text style={styles.text_title}>Mã môn hc: {item.code_exam}</Text>
<Text style={styles.text_title}>Mã lp: {item.code_class}</Text>
<View style={styles.content}>
<Text style={styles.text_content}>Ca/Tiết thi: {item.poetry}</Text>
<Text style={styles.text_content}>Th thi: {item.day_exam}</Text>
</View>
<View style={styles.content}>
<Text style={styles.text_content}>Ngày thi: {item.time_exam}</Text>
<Text style={styles.text_content}>Phòng thi: {item.class_exam}</Text>
</View>
<Text style={styles.text_content}>Ghi chú/ hình thc thi: {item.description}</Text>
</TouchableOpacity>
);
};
export default ItemView;
const styles = StyleSheet.create({
container: {
borderRadius: 10,
paddingHorizontal: 14,
paddingVertical: 10,
marginBottom: 10,
shadowColor: R.colors.black,
shadowOffset: {
width: 0,
},
shadowOpacity: 1,
shadowRadius: 5,
elevation: 5,
backgroundColor: R.colors.white,
marginVertical: 5,
marginHorizontal:10
},
content: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
text_title: {
fontSize: R.fontsize.fontsSize12,
fontWeight: '500',
color: R.colors.blue500,
fontFamily: R.fonts.InterSemiBold,
},
text_content: {
fontSize: R.fontsize.fontsSize12,
color: R.colors.black,
fontWeight: '400',
fontFamily: R.fonts.InterRegular,
},
});
import { StyleSheet } from 'react-native';
import R from "../../assets/R";
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: R.colors.white,
},
header: {
height: 60,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: 20,
borderBottomWidth: 1,
borderBottomColor: R.colors.grey_200,
},
title: {
fontSize: R.fontsize.fontsSize16,
fontWeight: '700',
fontFamily: R.fonts.InterSemiBold,
color: R.colors.black,
},
flatlist: {
flex: 1,
marginHorizontal: 5,
},
});
export default styles;
\ No newline at end of file
import React from 'react';
import {View, FlatList, SafeAreaView } from 'react-native';
import Header from "../../components/Header/Header";
import R from "../../assets/R";
import I18n from "../../helper/i18/i18n";
import ItemView from "./item_view";
import styles from './style';
import ItemCalendar from '../roll_call/item_calendar';
const ExamScheduleView = (props) => {
const {dataExamSchedule} = props;
const renderContent = ({ item }) => {
return (
<ItemView
item={item}
/>
);
};
return (
<SafeAreaView style={styles.container}>
<Header isBack title={I18n.t("ExamSchedule")} />
<View style={styles.container}>
<ItemCalendar/>
<View style={styles.flatlist}>
<FlatList
data={dataExamSchedule}
renderItem={renderContent}
keyExtractor={(item) => item.id}
showsVerticalScrollIndicator={false}
/>
</View>
</View>
</SafeAreaView>
);
};
export default ExamScheduleView;
import { Image, StyleSheet, TextInput, View } from "react-native";
import React from "react";
import R from "../../assets/R";
const customHeader = (props) => {
const {
pathLogo,
width,
height,
value,
textLabel,
onChangeText,
textColorLabel,
} = props;
const SearchIcon = R.images.icSearch;
return (
<View style={styles.container}>
<Image
source={pathLogo}
style={{ width: width, height: height }}
/>
<View style={styles.size_box}></View>
<View
style={styles.search_box}
>
<SearchIcon
width={25}
height={25}
stroke={R.colors.white}
stroke-width={2}
/>
<View style={styles.size_box_2}></View>
<TextInput
value={value}
onChangeText={onChangeText}
placeholder={textLabel}
placeholderTextColor={textColorLabel}
style={styles.input}
/>
</View>
</View>
);
};
export default customHeader;
const styles = StyleSheet.create({
container: {
flexDirection: "row",
marginHorizontal: 15,
marginTop: 15,
maxHeight: 40,
},
size_box:{
width: '20%',
},
size_box_2:{
width: '2.5%',
},
search_box: {
flex: 3,
flexDirection: "row",
backgroundColor: R.colors.black250,
alignItems: "center",
borderColor: R.colors.gray280,
borderRadius: 100,
borderWidth: 1,
paddingHorizontal: 14,
paddingVertical: 5
},
input: {
fontSize: R.fontsize.fontsSize12,
color: R.colors.white,
minHeight: 40,
flex: 1,
fontFamily: R.fonts.InterRegular,
fontWeight: '400',
},
});
import React, {Component} from 'react'; import React, { Component, useState } from "react";
import {View, Text} from 'react-native'; import { View, Text, Linking } from "react-native";
import * as SCREENNAME from '../../routers/ScreenNames'
import Homeview from "./view";
import R from "../../assets/R";
import { useNavigation } from '@react-navigation/native';
const Home = (props) => {
const navigation = useNavigation();
const [selectedMenuItem, setSelectedMenuItem] = useState("");
const [searchText, setSearchText] = useState("");
const [userProfile, setUserProfile] = useState({
name: "NGUYỄN MINH ĐỨC",
phone: "0895457",
avatar: null,
});
const menuDataStudy = [
{ id: 1, title: "Lịch học", icon: R.images.icCalendar, screenName: SCREENNAME.DRAWERNAVIGATION,action: 'NAVIGATE',},
{ id: 2, title: "Lịch thi", icon: R.images.icExamSchedule, screenName: SCREENNAME.EXAMSCHEDULE,action: 'NAVIGATE',params: { type: 'exam' }},
{ id: 3, title: "Điểm danh", icon: R.images.icRollCall , screenName: SCREENNAME.ROLLCALL,action: 'NAVIGATE',},
{ id: 4, title: "Điểm rèn luyện", icon: R.images.icTrainingPoint,screenName: SCREENNAME.TRAININGPOINT,action: 'NAVIGATE', },
];
const menuDataIndividual = [
{
id: 5,
title: "Đăng kí ngoại trú",
icon: R.images.icOutpatientRegistration,
screenName: SCREENNAME.OUTPATIENTINFOMATION,
action: 'NAVIGATE',
},
{ id: 6, title: "Việc làm", icon: R.images.icJob ,screenName: SCREENNAME.ELEARNINGSCREEN,action: 'NAVIGATE',},
{ id: 7, title: "Y tế", icon: R.images.icMedical,screenName: SCREENNAME.MEDICAL,action: 'NAVIGATE', },
];
const menuDataOnlineSer = [
{ id: 8, title: "Đăng kí chứng chỉ", icon: R.images.icRFCertificate,screenName: SCREENNAME.LISTCERTIFICATE,action: 'NAVIGATE', },
{ id: 9, title: "Công nợ", icon: R.images.icDebt,screenName: SCREENNAME.DEBT,action: 'NAVIGATE', },
{ id: 10, title: "Chương trình đào tạo", icon: R.images.icTrainingProgram ,screenName: SCREENNAME.TRAININGPROGRAM, action: 'NAVIGATE' },
{ id: 11, title: "Tài liệu học tập", icon: R.images.icStudyMaterials, action: 'OPEN_URL', url: 'https://job-portal.com' },
];
const actionHandlers = {
NAVIGATE: (item) => {
navigation.navigate(item.screenName, {
...item.params,
itemData: item,
title: item.title,
});
},
import Homeview from './view'; OPEN_URL: (item) => {
Linking.openURL(item.url).catch((err) => {
console.error("Failed to open URL:", err);
});
},
const Home = props => { SHOW_MODAL: (item) => {
return <Homeview />; console.log("Show modal for:", item.title);
},
CALL_API: (item) => {
console.log("Calling API for:", item.title);
},
};
const handleMenuItemPress = (item) => {
const handler = actionHandlers[item.action];
if (handler) {
handler(item);
} else {
console.warn(`No handler found for action: ${item.action}`);
}
};
const handleSearchChange = (text) => {
setSearchText(text);
};
return (
<Homeview
menuDataStudy={menuDataStudy}
menuDataIndividual={menuDataIndividual}
menuDataOnlineSer={menuDataOnlineSer}
selectedMenuItem={selectedMenuItem}
searchText={searchText}
userProfile={userProfile}
onMenuItemPress={handleMenuItemPress}
onSearchChange={handleSearchChange}
/>
);
}; };
export default Home; export default Home;
import React from "react";
import { View, Text, StyleSheet, Image, TouchableOpacity } from "react-native";
import R from "../../assets/R";
const ItemGrid = ({ item ,onPress }) => {
const IconComponent = item.icon;
return (
<TouchableOpacity
onPress={onPress}
style={styles.menu_item}
>
<View style={styles.icon_container}>
<IconComponent
width={35}
height={35}
/>
</View>
<Text style={styles.menu_text}>{item.title}</Text>
</TouchableOpacity>
);
};
export default ItemGrid;
const styles = StyleSheet.create({
menu_item: {
alignItems: "center",
paddingVertical: 10,
paddingHorizontal: 5,
marginHorizontal: 5,
marginVertical: 5,
flex: 1,
maxWidth: "30%",
minHeight:"30%"
},
icon_container: {
width: 35,
height: 35,
backgroundColor:R.colors.white,
},
menu_text: {
fontSize: R.fontsize.fontsSize10,
fontWeight: "600",
color: R.colors.black,
fontFamily: R.fonts.InterMedium,
textAlign: "center",
},
});
import { StyleSheet } from "react-native";
import R from "../../assets/R";
const styles = StyleSheet.create({
container_body: {
flex: 1,
backgroundColor: R.colors.white,
},
background_header: {
height: 295,
position: 'relative',
},
profile_card: {
position: 'absolute',
bottom: -30,
flexDirection: "row",
alignItems: "center",
backgroundColor: R.colors.white,
paddingVertical: 10,
paddingHorizontal: 15,
marginHorizontal: 15,
borderRadius: 15,
shadowColor: R.colors.black,
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.22,
shadowRadius: 2.22,
elevation: 3,
},
profile_left: {
flexDirection: "row",
alignItems: "center",
flex: 3,
},
avatar: {
width: 37,
height: 37,
borderRadius: 20,
backgroundColor: R.colors.white,
overflow: 'hidden',
},
avatar_image: {
width: '100%',
height: '100%',
},
avatar_placeholder: {
width: '100%',
height: '100%',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: R.colors.grey,
},
avatar_text: {
fontSize: R.fontsize.fontsSize16,
fontWeight: '600',
color: R.colors.white,
fontFamily: R.fonts.InterSemiBold,
},
information: {
flex: 2,
marginLeft: 5,
},
text_card_info: {
fontSize: R.fontsize.fontsSize12,
fontWeight: "400",
color: R.colors.black,
fontFamily: R.fonts.InterRegular,
numberOfLines: 1,
ellipsizeMode: "tail",
},
profile_btn: {
paddingVertical: 5,
paddingHorizontal: 10,
backgroundColor: R.colors.gray220,
minHeight: 21,
maxWidth: 108,
borderRadius: 15,
flexDirection: "row",
justifyContent: 'center',
alignItems: 'center',
},
btn_text: {
fontSize: R.fontsize.fontsSize10,
fontWeight: "400",
color: R.colors.black,
fontFamily: R.fonts.InterRegular,
},
icon: {
marginLeft: 5,
},
menu_container: {
marginHorizontal: 15,
},
menu_title: {
color: R.colors.txtMain,
fontSize: R.fontsize.fontsSize14,
fontWeight: "600",
color: R.colors.black,
fontFamily: R.fonts.InterMedium,
},
scroll: {
flex: 1,
marginTop: 35,
}
});
export default styles;
\ No newline at end of file
import React, {Component} from 'react'; import React from "react";
import {View, Text, SafeAreaView, StyleSheet} from 'react-native'; import {
import Header from '../../components/Header/Header'; View,
import i18n from '../../helper/i18/i18n'; Text,
import R from '../../assets/R'; ImageBackground,
const HomeView = props => { TouchableOpacity,
FlatList,
ScrollView,
TouchableWithoutFeedback,
Keyboard,
SafeAreaView,
Image,
} from "react-native";
import HeaderCus from "../home/header";
import R from "../../assets/R";
import I18n from "../../helper/i18/i18n";
import ItemGrid from "./item";
import styles from "./style";
import { useNavigation } from "@react-navigation/native";
import * as SCREENNAME from "../../routers/ScreenNames";
const HomeView = (props) => {
const {
menuDataStudy,
menuDataIndividual,
menuDataOnlineSer,
selectedMenuItem,
searchText,
userProfile,
onMenuItemPress,
onSearchChange,
} = props;
const ArrowRightIcon = R.images.icArrowRight;
const navigate = useNavigation();
const renderMenuItem = ({ item }) => {
return <ItemGrid item={item} onPress={() => onMenuItemPress(item)} />;
};
return ( return (
<SafeAreaView style={styles.container}> <TouchableWithoutFeedback onPress={Keyboard.dismiss} accessible={false}>
<View style={styles.container}> <SafeAreaView style={{ flex: 1 }}>
<Header title={i18n.t('Home')} />
<Text>Home screen</Text> <View style={styles.container_body}>
</View> <ImageBackground
</SafeAreaView> source={R.images.igBackgroundSlider}
style={styles.background_header}
>
<HeaderCus
pathLogo={R.images.igLogo}
width={75}
height={36}
textInput={R.colors.white}
backgroundInput={R.colors.backgroundInputSearch}
textLabel={I18n.t("Search")}
textColorLabel={R.colors.white}
/>
<View style={styles.profile_card}>
<View style={styles.profile_left}>
<View style={styles.avatar}>
{userProfile?.avatar ? (
<Image
source={{ uri: userProfile.avatar }}
style={styles.avatar_image}
resizeMode="cover"
/>
) : (
<View style={styles.avatar_placeholder}>
<Text style={styles.avatar_text}>
{userProfile?.name?.charAt(0)}
</Text>
</View>
)}
</View>
<View style={styles.information}>
<Text
style={styles.text_card_info}
numberOfLines={1}
ellipsizeMode="tail"
>
{userProfile?.name}
</Text>
<Text
style={styles.text_card_info}
numberOfLines={1}
ellipsizeMode="tail"
>
{userProfile?.phone}
</Text>
</View>
</View>
<TouchableOpacity style={styles.profile_btn} onPress={() => navigate.navigate(SCREENNAME.PROFILE)}>
<Text style={styles.btn_text}>H sơ cá nhân</Text>
<ArrowRightIcon
width={5}
height={10}
fill={R.colors.txtMain}
style={styles.icon}
/>
</TouchableOpacity>
</View>
</ImageBackground>
<ScrollView
showsVerticalScrollIndicator={false}
style={styles.scroll}>
<View style={styles.menu_container}>
<Text style={styles.menu_title}>Hc tp</Text>
<FlatList
data={menuDataStudy}
renderItem={renderMenuItem}
numColumns={3}
keyExtractor={(item) => item.id.toString()}
scrollEnabled={false}
columnWrapperStyle={styles.row}
/>
</View>
<View style={styles.menu_container}>
<Text style={styles.menu_title}>Cá nhân</Text>
<FlatList
data={menuDataIndividual}
renderItem={renderMenuItem}
numColumns={3}
keyExtractor={(item) => item.id.toString()}
scrollEnabled={false}
columnWrapperStyle={styles.row}
/>
</View>
<View style={styles.menu_container}>
<Text style={styles.menu_title}>Dch v trc tuyến</Text>
<FlatList
data={menuDataOnlineSer}
renderItem={renderMenuItem}
numColumns={3}
keyExtractor={(item) => item.id.toString()}
scrollEnabled={false}
columnWrapperStyle={styles.row}
/>
</View>
</ScrollView>
</View>
</SafeAreaView>
</TouchableWithoutFeedback>
); );
}; };
export default HomeView; export default HomeView;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: R.colors.white,
},
});
import {useNavigation} from '@react-navigation/native';
import React from 'react';
import {Text, View} from 'react-native';
import LoginView from './view';
import {TABNAVIGATOR} from '../../routers/ScreenNames';
const Login = ({params}) => {
const navigate = useNavigation();
const onLogin = () => {
navigate.navigate(TABNAVIGATOR);
console.log('Hello');
};
return <LoginView onLogin={onLogin} />;
};
export default Login;
import React from 'react';
import {Text, View, StyleSheet, SafeAreaView} from 'react-native';
import TextField from '../../components/Input/TextField';
import {useForm, Controller} from 'react-hook-form';
import R from '../../assets/R';
import Header from '../../components/Header/Header';
import Button from '../../components/Button';
const LoginView = ({onLogin}) => {
const {
control,
handleSubmit,
formState: {errors},
} = useForm();
return (
<SafeAreaView style={styles.container}>
<Header />
<View style={styles.container}>
<Controller
control={control}
rules={{
required: true,
maxLength: 225,
}}
render={({field: {onChange, onBlur, value}}) => (
<TextField
title={'Mã phẩm giống'}
required
value={value}
onChangeText={onChange}
error={errors.code}
/>
)}
name="code"
/>
<Button title={'Lưu'} onPress={handleSubmit(onLogin)} />
</View>
</SafeAreaView>
);
};
export default LoginView;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: R.colors.white,
},
});
import { StyleSheet, Text, View, TouchableOpacity, Image } from 'react-native';
import React from 'react';
import R from '../../assets/R';
const CardButtonImage = ({
onPress,
text = "Tải ảnh ở đây",
width,
height = 150,
disabled = false
}) => {
return (
<TouchableOpacity
style={[
styles.container_image,
{ width, height },
disabled && styles.disabled
]}
onPress={onPress}
disabled={disabled}
activeOpacity={0.7}
>
<View style={styles.image_placeholder}>
</View>
<View>
<Image
source={R.images.icImageDownload}
style={styles.image}
/>
</View>
<Text style={styles.placeholder_text}>
{text}
</Text>
</TouchableOpacity>
);
};
export default CardButtonImage;
const styles = StyleSheet.create({
container_image: {
justifyContent: 'center',
alignItems: 'center',
marginBottom:15,
borderWidth: 1,
borderColor: R.colors.blue500,
borderStyle: 'dashed',
borderRadius: 15,
backgroundColor: R.colors.white,
flexDirection:'row',
flex:1,
},
image_placeholder: {
position: 'absolute',
width: '100%',
height: '100%',
borderRadius: 10,
},
image: {
width:20,
height:20,
marginRight:5,
marginTop:5,
},
placeholder_text: {
fontSize: R.fontsize.fontsSize14,
fontWeight: '500',
color:R.colors.black,
textAlign: 'center',
letterSpacing: 0.3,
},
disabled: {
opacity: 0.5,
backgroundColor:R.colors.white,
}
});
\ No newline at end of file
import React from 'react';
import {Text, View, StyleSheet} from 'react-native';
import MedicalView from './view';
const Medical = (props) => {
return (
<MedicalView />
);
};
export default Medical;
import React from "react";
import {
Text,
View,
StyleSheet,
ScrollView,
SafeAreaView,
} from "react-native";
import Header from "../../components/Header/Header";
import R from "../../assets/R";
import DropdownSelect from "../../components/Dropdown/DropdownSel";
import CardButtonImage from "./card_button";
import Button from "../../components/Button";
import CustomTextInput from "../../components/Input/TextFieldCus";
const MedicalView = (props) => {
return (
<SafeAreaView style={[styles.container, { backgroundColor: R.colors.white, paddingHorizontal: 0, paddingTop: 0 }]}>
<Header title={"Thông tin y tế"} isBack />
<ScrollView showsVerticalScrollIndicator={false} style={{ paddingHorizontal: 15 }}>
<Text style={styles.title_header}>Thông tin bo him y tế</Text>
<CustomTextInput
title={"Mã BHYT hoặc BHXH"}
required={true}
/>
<DropdownSelect
title={"Loại thẻ BHYT *"}
titleMarginBottom={3}
marginHorizontal={0}
placeholder="Khác"
placeholderColor={R.colors.grey_800}
borderRadius={10}
height={40}
iconColor={R.colors.black}
titleFontWeight={"300"}
fontSize={R.fontsize.fontsSize12}
titleFontSize={R.fontsize.fontsSize12}
titleFontFamily={R.fonts.InterRegular}
/>
<CustomTextInput
title={"Ngày hết hạn BHYT"}
required={true}
/>
<Text style={styles.title_input}>
Hình chp mt trước BHYT, hoc hình chp kết qu tra cu th BHXH{" "}
<Text style={{ color: R.colors.red }}>*</Text>
</Text>
<CardButtonImage />
<Text style={styles.title_input}>
Hình chp kết qu tra cu hn th BHYT{" "}
<Text style={{ color: R.colors.red }}>*</Text>
</Text>
<CardButtonImage />
<DropdownSelect
title={
" Đăng ký nơi khám chữa bệnh ban đầu theo BHYT khi học tại trường *"
}
placeholder="HS"
placeholderColor={R.colors.grey_800}
borderRadius={10}
height={40}
iconColor={R.colors.black}
titleFontWeight={"300"}
fontSize={R.fontsize.fontsSize12}
titleFontSize={R.fontsize.fontsSize12}
titleFontFamily={R.fonts.InterRegular}
marginBottom={50}
marginHorizontal={0}
marginVertical={0}
/>
<Button
title={"Cập nhật"}
titleFontFamily={R.fonts.InterSemiBold}
titleFontSize={R.fontsize.fontsSize14}
titleFontWeight={"600"}
backgroundColor={R.colors.blue500}
borderRadius={10}
textColor={R.colors.white}
height={40}
marginBottom={15}
marginHorizontal={0}
marginVertical={0}
/>
</ScrollView>
</SafeAreaView>
);
};
export default MedicalView;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: R.colors.white,
},
title_header: {
color: R.colors.blue500,
fontFamily: R.fonts.InterSemiBold,
fontWeight: "600",
fontSize: R.fontsize.fontsSize14,
},
title_input: {
titleFontWeight: "300",
fontSize: R.fontsize.fontsSize12,
titleFontSize: R.fontsize.fontsSize12,
titleFontFamily: R.fonts.InterRegular,
color: R.colors.black,
},
});
import React, { useState } from "react";
import { Text, View } from "react-native";
import NewDetailsView from "./view";
const NewDetails = (props) => {
const [titleNewsDetail, setNewsDetail] = useState({
title:'Vì sao miền trung lại nóng nhất',
content:'Dãy Trường Sơn chắn gió: Gió phơn Tây Nam từ Lào vượt núi, khô và nóng khi xuống đồng bằng Hiệu ứng gió phơn: Làm không khí mất ẩm, tăng nhiệt độ nhanh chóng . Ít rừng che phủ + bề mặt đất đá: Tăng khả năng hấp thụ và giữ nhiệt.'
})
return <NewDetailsView
titleNewsDetail= {titleNewsDetail}
/>;
};
export default NewDetails;
import React from "react";
import { Text, View, TouchableOpacity, StyleSheet, SafeAreaView } from "react-native";
import Header from "../../components/Header/Header";
import R from "../../assets/R";
const NewDetails = (props) => {
const { titleNewsDetail } = props;
return (
<SafeAreaView style={styles.container}>
<Header isBack title={titleNewsDetail.title} />
<View
style={styles.container}
>
<Text style={styles.container_content}>
{titleNewsDetail.content}
</Text>
</View>
</SafeAreaView>
);
};
export default NewDetails;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: R.colors.white,
},
container_content: {
marginHorizontal: 15,
fontFamily: R.fonts.InterRegular,
fontSize: R.fontsize.fontsSize12,
fontWeight: '400',
color: R.colors.black
}
})
\ No newline at end of file
import React from 'react'; import React, { useState } from "react";
import {Text, View} from 'react-native'; import { Text, View } from "react-native";
import LoginView from './view'; import NotificationView from "./view";
const Login = ({params}) => <LoginView />; const Notification = (props) => {
const dataListTabView = [
{ key: "All", title: "Tất cả" },
{ key: "Study", title: "Học tập" },
{ key: "Activity", title: "Hoạt động" },
{ key: "Tuition", title: "Học phí" },
];
export default Login; const [activeTab, setActiveTab] = useState("All");
const [dataNotifi, setDataNotifi] = useState({
All: [
{
title_notifi: 'Thông báo nghỉ học',
message: 'Thông báo nghỉ học do tình hình bão số 4 phức tạp',
time: '14/4/2024'
},
{
title_notifi: 'Thông báo nghỉ học',
message: 'Thông báo nghỉ học do tình hình bão số 4 phức tạp',
time: '14/4/2024'
},
{
title_notifi: 'Thông báo nghỉ học',
message: 'Thông báo nghỉ học do tình hình bão số 4 phức tạp',
time: '14/4/2024'
},
{
title_notifi: 'Thông báo nghỉ học',
message: 'Thông báo nghỉ học do tình hình bão số 4 phức tạp',
time: '14/4/2024'
},
{
title_notifi: 'Thông báo nghỉ học',
message: 'Thông báo nghỉ học do tình hình bão số 4 phức tạp',
time: '14/4/2024'
},
{
title_notifi: 'Thông báo nghỉ học',
message: 'Thông báo nghỉ học do tình hình bão số 4 phức tạp',
time: '14/4/2024'
},
{
title_notifi: 'Thông báo nghỉ học',
message: 'Thông báo nghỉ học do tình hình bão số 4 phức tạp',
time: '14/4/2024'
},
{
title_notifi: 'Thông báo nghỉ học',
message: 'Thông báo nghỉ học do tình hình bão số 4 phức tạp',
time: '14/4/2024'
},
{
title_notifi: 'Thông báo nghỉ học',
message: 'Thông báo nghỉ học do tình hình bão số 4 phức tạp',
time: '14/4/2024'
},
{
title_notifi: 'Thông báo nghỉ học',
message: 'Thông báo nghỉ học do tình hình bão số 4 phức tạp',
time: '14/4/2024'
},
{
title_notifi: 'Thông báo nghỉ học',
message: 'Thông báo nghỉ học do tình hình bão số 4 phức tạp',
time: '14/4/2024'
},
],
Study: [],
Activity: [],
Tuition: [],
});
const handleTabChange = (tabKey) => {
setActiveTab(tabKey);
};
const getEmptyMessage = (tabKey) => {
const messages = {
All: "Chưa có thông báo nào",
Study: "Chưa có thông báo học tập",
Activity: "Chưa có thông báo hoạt động",
Tuition: "Chưa có thông báo học phí",
};
return messages[tabKey] || "Không có dữ liệu";
};
return (
<NotificationView
dataListTabView={dataListTabView}
activeTab={activeTab}
onTabChange={handleTabChange}
dataNotifi={dataNotifi}
getEmptyMessage={getEmptyMessage}
/>
);
};
export default Notification;
import { StyleSheet } from "react-native";
import R from "../../assets/R";
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: R.colors.white,
},
container_tab_bar: {
marginVertical: 10,
marginHorizontal: 12,
},
tab_button: {
paddingHorizontal: 5,
paddingVertical: 5,
marginHorizontal: 3,
borderRadius: 10,
backgroundColor: R.colors.gray400,
minWidth: 86,
alignItems: "center",
justifyContent: "center",
},
tab_button_active: {
backgroundColor: R.colors.blue500,
},
tab_button_text: {
fontFamily: R.fonts.InterSemiBold,
fontWeight: "500",
fontSize: R.fontsize.fontsSize12,
color: R.colors.white,
},
notification_item: {
backgroundColor: R.colors.white,
paddingVertical: 10,
marginHorizontal: 15,
marginBottom: 10,
paddingHorizontal: 15,
borderRadius: 10,
flexDirection: "row",
alignItems: "flex-start",
shadowColor: R.colors.black,
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 1,
shadowRadius: 2,
elevation: 1,
},
notification_content: {
flex: 1,
overflow: 'hidden',
backgroundColor: R.colors.white,
},
notification_title: {
fontSize: R.fontsize.fontsSize14,
fontWeight: "600",
fontFamily: R.fonts.InterMedium,
color: R.colors.black,
numberOfLines: 1,
ellipsizeMode: "tail",
lineHeight: 16
},
text_content: {
fontSize: R.fontsize.fontsSize12,
fontWeight: "400",
fontFamily: R.fonts.InterRegular,
color: R.colors.black,
lineHeight: 16
},
content_container: {
flex: 1,
backgroundColor: R.colors.white,
},
});
export default styles;
\ No newline at end of file
import React from 'react'; import React, { useState } from "react";
import {Text, View, TouchableOpacity} from 'react-native'; import {
Text,
const LoginView = ({params}) => ( View,
<View TouchableOpacity,
style={{ FlatList,
flex: 1, } from "react-native";
justifyContent: 'center', import Header from "../../components/Header/Header";
alignItems: 'center', import ItemEmpty from "../../components/List/ItemEmpty";
}}> import I18n from "../../helper/i18/i18n";
<TouchableOpacity> import styles from "./style";
<Text>Login</Text> import R from "../../assets/R";
import { useNavigation } from "@react-navigation/native";
import { NEWSDETAILS } from "../../routers/ScreenNames";
const NotificationView = (props) => {
const {
dataListTabView,
activeTab,
onTabChange,
dataNotifi,
getEmptyMessage,
} = props;
const navigation = useNavigation();
const renderTabViewItem = ({ item }) => {
const isActive = activeTab === item.key;
return (
<TouchableOpacity
style={[styles.tab_button, isActive && styles.tab_button_active]}
onPress={() => onTabChange(item.key)}
>
<Text
style={styles.tab_button_text}
>
{item.title}
</Text>
</TouchableOpacity>
);
};
const renderNotificationItem = ({ item }) => (
<TouchableOpacity style={styles.notification_item}
onPress={() => navigation.navigate(NEWSDETAILS, { item })}
>
<View style={styles.notification_content}>
<Text style={styles.notification_title} >{item.title_notifi}</Text>
<Text style={styles.text_content} numberOfLines={3} ellipsizeMode="tail">{item.message}</Text>
<Text style={[styles.text_content, { alignSelf: 'flex-end' }]} numberOfLines={1} ellipsizeMode="tail">{item.time}</Text>
</View>
</TouchableOpacity> </TouchableOpacity>
</View> );
); return (
<View style={styles.container}>
<Header
title={I18n.t("Notification")}
/>
<View style={styles.container_tab_bar}>
<FlatList
data={dataListTabView}
renderItem={renderTabViewItem}
keyExtractor={(item) => item.key}
horizontal
showsHorizontalScrollIndicator={false}
contentContainerStyle={styles.tabScrollContent}
/>
</View>
<View style={styles.content_container}>
<FlatList
data={dataNotifi[activeTab] || []}
showsVerticalScrollIndicator={false}
renderItem={renderNotificationItem}
keyExtractor={(item, index) => `${activeTab}-${index}`}
ListEmptyComponent={() => (
<ItemEmpty
title={getEmptyMessage(activeTab)}
/>
)}
/>
</View>
</View>
);
};
export default NotificationView;
export default LoginView;
import React, { useMemo, useState } from "react";
import { Text, View, StyleSheet } from "react-native";
import OutpatientInfomationView from "./view";
import R from "../../assets/R";
const OutpatientInfomation = (props) => {
const [isModalVisible, setIsModalVisible] = useState(false);
const handleOpenRegistrationModal = () => {
setIsModalVisible(true);
};
const handleCloseModal = () => {
setIsModalVisible(false);
};
const handleNavigateToOther = () => {
navigation?.navigate('OtherRegistrationScreen');
};
const handleSubmitRegistration = (selectedOptions) => {
console.log('Selected registration options:', selectedOptions);
};
const [dataTitle] = useState([
"STT",
"Địa chỉ",
"Quan hệ chủ hộ",
"Thời gian cư trú",
]);
const [listInfo, setListDataInfo] = useState([
{
address: "134/44/9 Nguyên xá, Bắc Từ Liêm, Hà Nội",
timeStay: "25/05/2025 - Hiện tại",
registrationDate: "23/05/2025",
status: "Đã phê duyệt",
relation: "Chủ trọ",
reason: "",
},
{
address: "23 Nguyễn Trãi, Thanh Xuân, Hà Nội",
timeStay: "10/03/2024 - 20/05/2025",
registrationDate: "05/03/2024",
status: "Chờ phê duyệt",
relation: "Anh/em",
reason: "Chuyển công tác",
},
{
address: "102/12 Trường Chinh, Đống Đa, Hà Nội",
timeStay: "15/01/2023 - 01/03/2024",
registrationDate: "12/01/2023",
status: "Từ chối",
relation: "Bạn bè",
reason: "Thông tin không khớp",
},
{
address: "102/12 Trường Chinh, Đống Đa, Hà Nội",
timeStay: "15/01/2023 - 01/03/2024",
registrationDate: "12/01/2023",
status: "Từ chối",
relation: "Bạn bè",
reason: "Thông tin không khớp",
},
{
address: "102/12 Trường Chinh, Đống Đa, Hà Nội",
timeStay: "15/01/2023 - 01/03/2024",
registrationDate: "12/01/2023",
status: "Từ chối",
relation: "Bạn bè",
reason: "Thông tin không khớp",
},
]);
const getStatusColor = (status) => {
switch (status) {
case 'Đã phê duyệt':
return R.colors.green ;
case 'Từ chối':
return R.colors.red ;
case 'Chờ phê duyệt':
return R.colors.orange ;
default:
return R.colors.black;
}
};
const processedData = useMemo(() => {
return listInfo.map((item) => ({
...item,
statusColor: getStatusColor(item.status),
}));
}, [listInfo]);
return <OutpatientInfomationView
dataTitle={dataTitle}
dataList={processedData}
isModalVisible={isModalVisible}
onOpenRegistrationModal={handleOpenRegistrationModal}
onCloseModal={handleCloseModal}
onNavigateToOther={handleNavigateToOther}
onSubmitRegistration={handleSubmitRegistration}
/>;
};
export default OutpatientInfomation;
import { StyleSheet, Text, View } from "react-native";
import React from "react";
import R from "../../assets/R";
const ItemNav = ({ item }) => {
return (
<View style={styles.container}>
<Text style={styles.text}>{item.address}</Text>
<Text style={[styles.text,{fontFamily:R.fonts.InterRegular}]}>Thi gian c trú: {item.timeStay}</Text>
<View style={{ flexDirection: "row"}}>
<Text style={[styles.text, { flex: 3,fontFamily:R.fonts.InterRegular }]}>Ngày đăng kí: {item.registrationDate}</Text>
<View style={[styles.text, { flex:2.5,fontFamily:R.fonts.InterRegular }]}>
<Text
style={[
styles.text,
{alignSelf:'flex-end'},
item.statusColor && { color: item.statusColor },
]}
>
{item.status}
</Text>
</View>
</View>
<Text style={[styles.text, { fontFamily:R.fonts.InterRegular }]}>Quan h vi ch tr:{item.relation}</Text>
<Text style={[styles.text, { fontFamily:R.fonts.InterRegular }] }>Lý do: {item.reason}</Text>
</View>
);
};
export default ItemNav;
const styles = StyleSheet.create({
container: {
backgroundColor: R.colors.white ,
borderRadius: 15,
marginBottom: 18,
padding: 15,
elevation: 2,
shadowColor: R.colors.black,
shadowOffset: {
width: 0,
height: 2,
},
shadowRadius: 2,
borderWidth: 1,
borderColor: R.colors.grey_50,
},
text: {
fontSize: R.fontsize.fontsSize12,
fontWeight: "600",
color: R.colors.black,
fontFamily: R.fonts.InterMedium,
},
});
import React, { useState } from "react";
import {
Modal,
View,
Text,
StyleSheet,
TouchableOpacity,
ScrollView,
Alert,
} from "react-native";
import R from "../../assets/R";
import Checkbox from "../../components/CheckBox";
import Button from "../../components/Button";
import DropdownSelect from "../../components/Dropdown/DropdownSel";
import CustomTextInput from "../../components/Input/TextFieldCus";
const RegistrationModal = ({ visible, onClose, onNavigateToOther }) => {
const [selectedAccommodationType, setSelectedAccommodationType] =
useState("other");
const handleAccommodationTypeChange = (type) => {
setSelectedAccommodationType(type);
};
return (
<Modal
visible={visible}
animationType="slide"
transparent={true}
onRequestClose={onClose}
>
<View style={styles.container_modal_overlay}>
<View style={styles.container_card}>
<View style={styles.header}>
<Text style={styles.text_header}>Đăng kí thông tin ngoi trú</Text>
</View>
<View style={{ flexDirection: "row", marginTop: 15 }}>
<View style={{ flex: 0.3 }}>
<Checkbox
label={"Khác"}
labelStyle={styles.text_checkbox}
size={12}
borderRadius={50}
borderWidth={1}
borderColor={R.colors.black}
isCheck={selectedAccommodationType === "other"}
onPress={() => handleAccommodationTypeChange("other")}
/>
</View>
<View style={{ flex: 1 }}>
<Checkbox
label={"Kí túc xá DHQG-HCM"}
labelStyle={styles.text_checkbox}
size={12}
borderRadius={50}
borderWidth={1}
onPress={() => handleAccommodationTypeChange("dormitory")}
isCheck={selectedAccommodationType === "dormitory"}
borderColor={R.colors.black}
/>
</View>
</View>
{selectedAccommodationType === "other" && (
<>
<View style={styles.container_row}>
<View style={{ flex: 1 }}>
<CustomTextInput
placeholder={"Họ và tên"}
title={"Tên chủ hộ"}
required={true}
/>
</View>
<View style={{ width: '3%' }} ></View>
<View style={{ flex: 1 }}>
<CustomTextInput
title={"Số nhà"}
required={true}
/>
</View>
</View>
<View style={styles.container_row}>
<View style={{ flex: 1 }}>
<CustomTextInput
placeholder={"Xã"}
title={"Xã"}
required={true}
/>
</View>
<View style={{ width: '3%' }} ></View>
<View style={{ flex: 1 }}>
<CustomTextInput
placeholder={"Tỉnh thành phố"}
title={"Tỉnh thành phố"}
required={true}
/>
</View>
</View>
<View style={styles.container_row}>
<View style={{ flex: 1 }}>
<CustomTextInput
title={"Quan hệ với chủ hộ"}
required={true}
/>
</View>
<View style={{ width: '3%' }} ></View>
<View style={{ flex: 1 }}>
<CustomTextInput
title={"TG bắt đầu cư trú"}
required={true}
/>
</View>
</View>
<View style={{ maxWidth: '90%', marginBottom: 10 }}>
<CustomTextInput
title={"Địa chỉ chi tiết"}
required={true}
/>
</View>
</>
)}
{selectedAccommodationType === "dormitory" && (
<View>
<View style={styles.container_row}>
<View style={{ flex: 1 }}>
<DropdownSelect
title={"Vị trí *"}
placeholder=""
placeholderFontFamily={R.fonts.InterRegular}
placeholderFontSize={R.fontsize.fontsSize12}
placeholderFontWeight={"400"}
placeholderColor={R.colors.grey_800}
borderRadius={10}
height={40}
iconColor={R.colors.black}
iconSize={10}
marginHorizontal={0}
marginVertical={0}
titleFontWeight={"400"}
fontSize={R.fontsize.fontsSize12}
titleFontSize={R.fontsize.fontsSize12}
titleFontFamily={R.fonts.InterRegular}
/>
</View>
<View style={{ width: '3%' }} ></View>
<View style={{ flex: 1 }}>
<DropdownSelect
title={"Tòa nhà *"}
placeholder=""
placeholderFontFamily={R.fonts.InterRegular}
placeholderFontSize={R.fontsize.fontsSize12}
placeholderFontWeight={"400"}
placeholderColor={R.colors.grey_800}
borderRadius={10}
height={40}
iconColor={R.colors.black}
iconSize={10}
marginHorizontal={0}
marginVertical={0}
titleFontWeight={"400"}
fontSize={R.fontsize.fontsSize12}
titleFontSize={R.fontsize.fontsSize12}
titleFontFamily={R.fonts.InterRegular}
/>
</View>
</View>
<View style={styles.container_row}>
<View style={{ flex: 1 }}>
<CustomTextInput
title={"Số phòng"}
required={true}
/>
</View>
<View style={{ width: '3%' }}></View>
<View style={{ flex: 1 }}>
<CustomTextInput
title={"TG bắt đầu cư trú"}
required={true}
/>
</View>
</View>
<CustomTextInput
title={"Địa chỉ chi tiết"}
required={true}
/>
</View>
)}
<View style={{ flexDirection: "row", alignSelf: "flex-end" , marginVertical:5}}>
<View>
<Button
backgroundColor={R.colors.white}
borderWidth={1}
borderRadius={10}
borderColor={R.colors.blue500}
textColor={R.colors.blue500}
fontWeight={'400'}
fontFamily={R.fonts.InterRegular}
title={"Huỷ"}
marginHorizontal={0}
marginVertical={0}
width={51}
height={31}
fontSize={R.fontsize.fontsSize12}
onPress={onClose}
/>
</View>
<View style={{ width: '3%' }}></View>
<View>
<Button
backgroundColor={R.colors.blue500}
borderWidth={1}
borderRadius={10}
borderColor={R.colors.blue500}
textColor={R.colors.white}
fontWeight={'400'}
fontFamily={R.fonts.InterRegular}
title={"Đăng kí mới"}
marginHorizontal={0}
marginVertical={0}
width={100}
height={31}
fontSize={R.fontsize.fontsSize12}
onPress={() => {
if (selectedAccommodationType === "other") {
console.log("Đăng ký ngoại trú");
} else {
console.log("Chuyển đến đăng ký kí túc xá");
}
}}
/>
</View>
</View>
</View>
</View>
</Modal>
);
};
export default RegistrationModal;
const styles = StyleSheet.create({
container_modal_overlay: {
flex: 1,
backgroundColor: R.colors.black_500,
justifyContent: "center",
alignItems: "center",
},
container_card: {
minWidth: "90%",
maxHeight: "80%",
backgroundColor: R.colors.white,
borderRadius: 10,
elevation: 10,
paddingHorizontal: 15,
paddingVertical: 10,
},
header: {
alignItems: "center",
},
text_header: {
color: R.colors.blue500,
fontFamily: R.fonts.InterMedium,
fontWeight: "500",
fontSize: R.fontsize.fontsSize18,
},
text_checkbox: {
fontSize: R.fontsize.fontsSize12,
fontFamily: R.fonts.InterRegular,
color: R.colors.black,
},
container_row: {
flexDirection: "row",
},
text_dormitory_info: {
fontSize: R.fontsize.fontsSize12,
fontFamily: R.fonts.InterRegular,
color: R.colors.black,
textAlign: "center",
lineHeight: 20,
},
});
import React from "react";
import {
Text,
View,
StyleSheet,
FlatList,
SafeAreaView,
} from "react-native";
import R from "../../assets/R";
import Header from "../../components/Header/Header";
import Button from "../../components/Button";
import ItemNav from "./item";
import RegistrationModal from "./modal";
const OutpatientInfomationView = (props) => {
const { dataTitle, dataList,isModalVisible,
onOpenRegistrationModal,
onCloseModal,
onNavigateToOther,
onSubmitRegistration,} = props;
const renderItem = ({ item, index }) => {
return <ItemNav item={item} />;
};
const keyExtractor = (item, index) => `history-item-${index}`;
return (
<SafeAreaView style={styles.container}>
<Header title={"Thông tin ngoại trú"} isBack />
<View style={styles.container}>
<View style={styles.container}>
<View style={styles.fixedInfoSection}>
<Text style={styles.text}>Thông tin tm trú</Text>
<Text style={[styles.text, styles.addressText]}>
134/44/9 Nguyên xá, Bc T Liêm, Hà Ni
</Text>
<Text style={[styles.text, styles.addressText]}>
Thi gian bt đầu cư chú: 20/11/2024
</Text>
<Text style={[styles.text, styles.historyTitle]}>
Lch s đăng ký
</Text>
</View>
<FlatList
style={styles.scrollableList}
data={dataList}
renderItem={renderItem}
keyExtractor={keyExtractor}
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.flatListContent}
removeClippedSubviews={true}
maxToRenderPerBatch={5}
windowSize={5}
initialNumToRender={3}
/>
<View style={styles.buttonSection}>
<Button
title={"Đăng kí mới"}
fontSize={R.fontsize.fontSizeHeader}
fontWeight={'300'}
textColor={R.colors.white}
fontFamily={R.fonts.InterRegular}
backgroundColor={R.colors.blue500}
borderRadius={10}
height={38}
onPress={onOpenRegistrationModal}
/>
</View>
</View>
<RegistrationModal
visible={isModalVisible}
onClose={onCloseModal}
onNavigateToOther={onNavigateToOther}
onSubmit={onSubmitRegistration}
/>
</View>
</SafeAreaView>
);
};
export default OutpatientInfomationView;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: R.colors.white,
},
fixedInfoSection: {
paddingHorizontal: 15,
paddingVertical: 10,
backgroundColor: R.colors.white,
},
scrollableList: {
flex: 1,
paddingHorizontal: 15,
},
flatListContent: {
paddingTop: 10,
paddingBottom: 10,
},
buttonSection: {
paddingHorizontal: 15,
paddingVertical: 15,
backgroundColor: R.colors.white,
},
text: {
color: R.colors.blue500,
fontFamily: R.fonts.InterSemiBold,
fontWeight: "500",
fontSize: R.fontsize.fontsSize14,
},
addressText: {
fontSize: R.fontsize.fontsSize12,
color: R.colors.black,
fontFamily: R.fonts.InterRegular,
fontWeight: "400",
},
historyTitle: {
fontSize: R.fontsize.fontsSize14,
},
});
\ No newline at end of file
import React, {useState} from 'react';
import {Text, View, StyleSheet} from 'react-native';
import ProfileView from './view';
const Profile = (props) => {
const [phone, setPhone] = useState('');
const [cmnd, setCmnd] = useState('');
const [bank, setBank] = useState('');
return (
<ProfileView phone={phone} setPhone={setPhone} cmnd={cmnd} setCmnd={setCmnd} bank={bank} setBank={setBank}/>
);
};
export default Profile;
import React from "react";
import {
Text,
View,
TouchableOpacity,
StyleSheet,
ScrollView,
SafeAreaView,
TextInput,
} from "react-native";
import R from "../../assets/R";
import Header from "../../components/Header/Header";
import Button from "../../components/Button";
import DropdownSelect from "../../components/Dropdown/DropdownSel";
import Checkbox from "../../components/CheckBox";
import CustomTextInput from "../../components/Input/TextFieldCus";
const ProfileView = (props) => {
const { phone, setPhone, cmnd, setCmnd, bank, setBank } = props;
const IconCamera = R.images.icCamera;
return (
<SafeAreaView style={{ flex: 1, backgroundColor: R.colors.white }}>
<Header title={"Hồ sơ cá nhân"} isBack />
<ScrollView showsVerticalScrollIndicator={false} style={{ flex: 1 }}>
<View style={styles.container}>
<View style={styles.body_header}>
<View style={styles.container_image}>
<TouchableOpacity style={styles.images}>
<View style={{ alignItems: "center" }}>
<IconCamera />
</View>
</TouchableOpacity>
<Button
width={110}
height={30}
marginHorizontal={0}
marginTop={11}
borderRadius={10}
textColor={R.colors.white}
fontFamily={R.fonts.InterRegular}
fontSize={R.fontsize.fontsSize12}
fontWeight={"400"}
backgroundColor={R.colors.main}
title={"Chọn ảnh"}
/>
</View>
<View style={styles.body_info_user}>
<Text style={styles.text}>H và tên: Nguyn Minh Duy</Text>
<Text style={styles.text}>Gii tính: Nam</Text>
<Text style={styles.text}>Năm sinh : 23/10/2003</Text>
<Text style={styles.text}>Mã sinh viên: PH32251</Text>
<Text style={styles.text}>Khoa: Khoa hc máy tính</Text>
<Text style={styles.text}>Chuyên ngành lp trình web</Text>
<View style={{ marginTop: 15 }}>
<Text style={styles.text_label}>
nh chp thng, nn TRNG hoc XANH, mc áo có c; t l 4x6,
dung lượng t 200 đến 500 KB
</Text>
</View>
</View>
</View>
<View style={styles.body}>
<View style={{ flexDirection: "row" }}>
<View style={{ flex: 1 }}>
<DropdownSelect
title={"Dân tộc *"}
placeholder="Kinh"
placeholderColor={R.colors.gray800}
placeholderFontFamily={R.fonts.InterRegular}
placeholderFontSize={R.fontsize.fontsSize10}
placeholderFontWeight={"400"}
borderRadius={10}
height={40}
iconSize={10}
iconColor={R.colors.black}
titleFontWeight={"400"}
titleFontSize={R.fontsize.fontsSize12}
fontSize={R.fontsize.fontsSize12}
titleFontFamily={R.fonts.InterRegular}
marginHorizontal={0}
marginVertical={0}
/>
</View>
<View style={{ width: '5%' }}></View>
<View style={{ flex: 1 }}>
<DropdownSelect
title={"Tôn giáo *"}
placeholder="Không"
placeholderColor={R.colors.gray800}
placeholderFontFamily={R.fonts.InterRegular}
placeholderFontSize={R.fontsize.fontsSize10}
placeholderFontWeight={"400"}
borderRadius={10}
height={40}
iconSize={10}
iconColor={R.colors.black}
titleFontWeight={"400"}
titleFontSize={R.fontsize.fontsSize12}
fontSize={R.fontsize.fontsSize12}
titleFontFamily={R.fonts.InterRegular}
marginHorizontal={0}
marginVertical={0}
/>
</View>
</View>
<View
style={styles.container_body}
>
<View style={{ flex: 1 }}>
<DropdownSelect
title={"Thành phần xuất thân *"}
titleFontWeight={"400"}
titleFontSize={R.fontsize.fontsSize12}
titleFontFamily={R.fonts.InterRegular}
placeholder="Khác"
placeholderColor={R.colors.gray800}
placeholderFontFamily={R.fonts.InterRegular}
placeholderFontSize={R.fontsize.fontsSize10}
placeholderFontWeight={"400"}
borderRadius={10}
height={40}
iconSize={10}
iconColor={R.colors.black}
fontSize={R.fontsize.fontsSize12}
marginHorizontal={0}
marginVertical={0}
/>
</View>
<View style={{ width: '5%' }}></View>
<View style={{ flex: 1 }}>
<DropdownSelect
title={"Nơi sinh *"}
placeholder="Tỉnh kiên giang"
placeholderColor={R.colors.gray800}
placeholderFontFamily={R.fonts.InterRegular}
placeholderFontSize={R.fontsize.fontsSize10}
placeholderFontWeight={"400"}
fontSize={R.fontsize.fontsSize12}
borderRadius={10}
backgroundColor={R.colors.greyBlue_soft_50}
height={40}
iconSize={10}
iconColor={R.colors.black}
titleFontWeight={"400"}
titleFontSize={R.fontsize.fontsSize12}
titleFontFamily={R.fonts.InterRegular}
marginHorizontal={0}
marginVertical={0}
/>
</View>
</View>
<CustomTextInput
title={"Điện thoại cá nhân "}
placeholder="Điện thoại cá nhân"
required={true}
value={phone}
onChangeText={setPhone}
keyboardType="phone-pad"
/>
<CustomTextInput
title={"CMND/CCCD"}
placeholder="CMND/CCCD"
required={true}
value={cmnd}
onChangeText={setCmnd}
keyboardType="phone-pad"
backgroundColor={R.colors.greyBlue_soft_50}
/>
<CustomTextInput
title={"Ngày cấp CMND/CCCD"}
placeholder="17/01/2021"
required={true}
value={cmnd}
onChangeText={setCmnd}
keyboardType="default"
backgroundColor={R.colors.greyBlue_soft_50}
/>
<CustomTextInput
title={"Nơi cấp CMND/CCCD"}
placeholder="Cục cảnh sát quản lý hành chính Hà Nội"
required={true}
value={cmnd}
onChangeText={setCmnd}
keyboardType="phone-pad"
backgroundColor={R.colors.greyBlue_soft_50}
/>
<Text style={[styles.text, { lineHeight: 16 }]}>
Thông tin tài khon ngân hàng ca sinh viên (Tài khon chính ch
ca sinh viên, không được là s tài khon ca người khác)
</Text>
<CustomTextInput
title={"Thông tin ngân hàng"}
placeholder="Thông tin ngân hàng"
required={true}
value={bank}
onChangeText={setBank}
/>
<CustomTextInput
title={"Số tài khoản"}
placeholder="Số tài khoản"
required={true}
value={bank}
onChangeText={setBank}
/>
<CustomTextInput
title={"Chi nhánh"}
placeholder="Chi nhánh"
required={true}
value={bank}
onChangeText={setBank}
/>
<CustomTextInput
title={"Email cá nhân"}
placeholder="Email cá nhân"
required={true}
value={bank}
onChangeText={setBank}
/>
<CustomTextInput
title={"Địa chỉ facebook"}
placeholder="Địa chỉ facebook"
required={true}
value={bank}
onChangeText={setBank}
/>
<CustomTextInput
title={"Địa chỉ thường trú"}
placeholder="Địa chỉ thường trú"
required={true}
value={bank}
onChangeText={setBank}
/>
<DropdownSelect
title={"Tỉnh/ TP-54"}
placeholder="Hà Nội"
placeholderColor={R.colors.gray800}
borderRadius={10}
iconSize={10}
placeholderFontFamily={R.fonts.InterRegular}
placeholderFontSize={R.fontsize.fontsSize10}
placeholderFontWeight={"400"}
height={40}
marginHorizontal={0}
iconColor={R.colors.black}
titleFontWeight={"400"}
titleFontSize={R.fontsize.fontsSize12}
fontSize={R.fontsize.fontsSize12}
marginVertical={5}
titleFontFamily={R.fonts.InterRegular}
backgroundColor={R.colors.greyBlue_soft_50}
/>
<DropdownSelect
title={"Phường/Xã"}
placeholder="Hà Nội"
placeholderColor={R.colors.gray800}
borderRadius={10}
iconSize={10}
placeholderFontFamily={R.fonts.InterRegular}
placeholderFontSize={R.fontsize.fontsSize10}
placeholderFontWeight={"400"}
height={40}
marginHorizontal={0}
iconColor={R.colors.black}
titleFontWeight={"400"}
titleFontSize={R.fontsize.fontsSize12}
fontSize={R.fontsize.fontsSize12}
titleFontFamily={R.fonts.InterRegular}
marginVertical={5}
backgroundColor={R.colors.greyBlue_soft_50}
/>
<View style={styles.container_body_footer}>
<Checkbox
label={"Đoàn viên?"}
labelStyle={styles.text_check_box}
size={14}
borderColor={R.colors.blue500}
/>
<View style={{ flex: 1, marginLeft: 16 }}>
<Text style={styles.text_check_box_label}>
Ngày vào đoàn
</Text>
</View>
<View style={{ flex: 1.2 }}>
<CustomTextInput
placeholder="Ngày vào đoàn"
required={true}
value={bank}
onChangeText={setBank}
/>
</View>
</View >
<View style={styles.container_body_footer}>
<Checkbox
label={"Đảng viên?"}
labelStyle={styles.text_check_box}
size={14}
borderColor={R.colors.blue500}
/>
<View style={{ flex: 1, marginLeft: 16 }}>
<Text style={styles.text_check_box_label}>
Ngày vào đảng
</Text>
</View>
<View style={{ flex: 1.2 }}>
<CustomTextInput
placeholder="Ngày vào đoàn"
required={true}
value={bank}
onChangeText={setBank}
/>
</View>
</View>
<Button
title={'Cập nhật'}
onPress={() => {}}
backgroundColor={R.colors.blue500}
height={30}
textStyle={styles.text_button}
marginHorizontal={0}
marginVertical={0}
borderRadius={10}
textColor={R.colors.white}
/>
</View>
</View>
</ScrollView>
</SafeAreaView>
);
};
export default ProfileView;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: R.colors.white,
paddingHorizontal: 15,
paddingTop: 15,
paddingBottom: 9,
},
body_header: {
flexDirection: "row",
},
container_image: {
flex: 1,
alignItems: "flex-start",
justifyContent: "flex-start",
},
container_body: {
flexDirection: "row",
marginTop: 10,
marginBottom: 5
},
images: {
backgroundColor: R.colors.grey_50,
minWidth: 108,
minHeight: 177,
borderRadius: 15,
borderWidth: 1,
borderColor: R.colors.grey_200,
justifyContent: "center",
alignItems: "center",
},
body_info_user: {
flex: 1.8,
},
text: {
fontFamily: R.fonts.InterRegular,
fontWeight: "400",
fontSize: R.fontsize.fontsSize12,
color: R.colors.blue500,
lineHeight: 24
},
text_label: {
fontFamily: R.fonts.InterRegular,
fontWeight: "400",
fontSize: R.fontsize.fontsSize12,
color: R.colors.grey_100,
},
text_button: {
fontFamily: R.fonts.InterRegular,
fontWeight: "400",
fontSize: R.fontsize.fontsSize12,
color: R.colors.white,
},
body: {
marginTop: 11,
},
text_check_box_label: {
fontFamily: R.fonts.InterRegular,
fontWeight: "400",
fontSize: R.fontsize.fontsSize12,
color: R.colors.black_800,
alignSelf: "center"
},
text_check_box: {
fontFamily: R.fonts.InterRegular,
fontWeight: "400",
fontSize: R.fontsize.fontsSize12,
color: R.colors.blue500,
},
container_body_footer: {
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
marginVertical: 5,
},
});
import React from 'react';
import {Text, View} from 'react-native';
import QrCodeView from './view';
const QrCode = ({params}) => <QrCodeView />;
export default QrCode;
import React from 'react';
import {Text, View, TouchableOpacity} from 'react-native';
const QrCodeView = ({params}) => (
<View
style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}}>
<TouchableOpacity>
<Text>QrCode</Text>
</TouchableOpacity>
</View>
);
export default QrCodeView;
import React, { useMemo, useState } from 'react';
import {Text, View, StyleSheet} from 'react-native';
import DetailRollCallView from './view';
import R from '../../../assets/R';
const DetailRollCall = (props) => {
const [dataListInfo,setDataListInfo] = useState([
{serial_number:'1', study_day:'10/07/2025',study_time:"7:30-9:30", checked_by:'ducnm',attendance_status:'Có mặt',note:''},
{serial_number:'2', study_day:'10/07/2025',study_time:"7:30-9:30", checked_by:'ducnm',attendance_status:'Vắng mặt',note:''},
{serial_number:'3', study_day:'10/07/2025',study_time:"7:30-9:30", checked_by:'ducnm',attendance_status:'Có mặt',note:''},
{serial_number:'4', study_day:'10/07/2025',study_time:"7:30-9:30", checked_by:'ducnm',attendance_status:'Có mặt',note:''},
{serial_number:'5', study_day:'10/07/2025',study_time:"7:30-9:30", checked_by:'ducnm',attendance_status:'Có mặt',note:''},
{serial_number:'6', study_day:'10/07/2025',study_time:"7:30-9:30", checked_by:'ducnm',attendance_status:'Có mặt',note:''},
{serial_number:'7', study_day:'10/07/2025',study_time:"7:30-9:30", checked_by:'ducnm',attendance_status:'Có mặt',note:''},
{serial_number:'8', study_day:'10/07/2025',study_time:"7:30-9:30", checked_by:'ducnm',attendance_status:'Có mặt',note:''},
{serial_number:'9', study_day:'10/07/2025',study_time:"7:30-9:30", checked_by:'ducnm',attendance_status:'Có mặt',note:''},
{serial_number:'10', study_day:'10/07/2025',study_time:"7:30-9:30", checked_by:'ducnm',attendance_status:'Có mặt',note:''},
{serial_number:'11', study_day:'10/07/2025',study_time:"7:30-9:30", checked_by:'ducnm',attendance_status:'Có mặt',note:''},
{serial_number:'12', study_day:'10/07/2025',study_time:"7:30-9:30", checked_by:'ducnm',attendance_status:'Có mặt',note:''},
{serial_number:'13', study_day:'10/07/2025',study_time:"7:30-9:30", checked_by:'ducnm',attendance_status:'Có mặt',note:''},
{serial_number:'14', study_day:'10/07/2025',study_time:"7:30-9:30", checked_by:'ducnm',attendance_status:'Có mặt',note:''},
{serial_number:'15', study_day:'10/07/2025',study_time:"7:30-9:30", checked_by:'ducnm',attendance_status:'Có mặt',note:''},
{serial_number:'16', study_day:'10/07/2025',study_time:"7:30-9:30", checked_by:'ducnm',attendance_status:'Có mặt',note:''},
{serial_number:'17', study_day:'10/07/2025',study_time:"7:30-9:30", checked_by:'ducnm',attendance_status:'Có mặt',note:''},
{serial_number:'18', study_day:'10/07/2025',study_time:"7:30-9:30", checked_by:'ducnm',attendance_status:'Có mặt',note:''},
{serial_number:'19', study_day:'10/07/2025',study_time:"7:30-9:30", checked_by:'ducnm',attendance_status:'Có mặt',note:''},
{serial_number:'20', study_day:'10/07/2025',study_time:"7:30-9:30", checked_by:'ducnm',attendance_status:'Có mặt',note:''},
{serial_number:'21', study_day:'10/07/2025',study_time:"7:30-9:30", checked_by:'ducnm',attendance_status:'Có mặt',note:''},
{serial_number:'22', study_day:'10/07/2025',study_time:"7:30-9:30", checked_by:'ducnm',attendance_status:'Có mặt',note:''},
{serial_number:'23', study_day:'10/07/2025',study_time:"7:30-9:30", checked_by:'ducnm',attendance_status:'Có mặt',note:''},
{serial_number:'24', study_day:'10/07/2025',study_time:"7:30-9:30", checked_by:'ducnm',attendance_status:'Có mặt',note:''},
{serial_number:'25', study_day:'10/07/2025',study_time:"7:30-9:30", checked_by:'ducnm',attendance_status:'Có mặt',note:''},
{serial_number:'26', study_day:'10/07/2025',study_time:"7:30-9:30", checked_by:'ducnm',attendance_status:'Có mặt',note:''},
{serial_number:'27', study_day:'10/07/2025',study_time:"7:30-9:30", checked_by:'ducnm',attendance_status:'Có mặt',note:''},
{serial_number:'28', study_day:'10/07/2025',study_time:"7:30-9:30", checked_by:'ducnm',attendance_status:'Có mặt',note:''},
{serial_number:'29', study_day:'10/07/2025',study_time:"7:30-9:30", checked_by:'ducnm',attendance_status:'Có mặt',note:''},
{serial_number:'30', study_day:'10/07/2025',study_time:"7:30-9:30", checked_by:'ducnm',attendance_status:'Có mặt',note:''},
])
// const dataList = dataListInfo.map(item => [
// item.serial_number,
// item.study_day,
// item.study_time,
// item.checked_by,
// item.attendance_status,
// item.note
// ]);
const [dataTitle,setDataTitle] = useState([
'STT','Ngày học','Thời gian học', 'Người điểm danh', 'Trạng thái', 'Ghi chú'
])
const getStatusColor = (attendanceStatus) => {
switch (attendanceStatus) {
case 'Có mặt':
return R.colors.green;
case 'Vắng mặt':
return R.colors.red ;
default:
return R.colors.black;
}
};
const processedTableData = useMemo(() => {
return dataListInfo.map((item, rowIndex) => {
return [
{
content: item.serial_number,
},
{
content: item.study_day,
},
{
content: item.study_time,
},
{
content: item.checked_by,
},
{
content: item.attendance_status,
style: {
color: getStatusColor(item.attendance_status),
fontWeight: '400'
}
},
{
content: item.note,
}
];
});
}, [dataListInfo]);
const handleRowPress = (rowIndex) => {
console.log('Row pressed:', rowIndex);
};
return (
<DetailRollCallView
dataTitle={dataTitle}
dataListInfo={processedTableData}
handleRowPress={handleRowPress}
/>
);
};
export default DetailRollCall;
import React from "react";
import {
Text,
View,
StyleSheet,
SafeAreaView,
ScrollView,
Dimensions,
} from "react-native";
import {
Table,
TableWrapper,
Row,
Cell,
} from 'react-native-reanimated-table';
import Header from "../../../components/Header/Header";
import R from "../../../assets/R";
const DetailRollCallView = (props) => {
const { dataTitle, dataListInfo } = props;
const STT_COLUMN_WIDTH = 40;
const DEFAULT_COLUMN_WIDTH = 140;
const getColumnWidths = () => {
return dataTitle.map((title, index) => {
return index === 0 ? STT_COLUMN_WIDTH : DEFAULT_COLUMN_WIDTH;
});
};
const columnWidths = getColumnWidths();
const totalTableWidth = columnWidths.reduce((sum, width) => sum + width, 0);
const renderCustomHeader = () => {
return (
<View style={styles.customHeaderRow}>
{dataTitle.map((title, index) => (
<View
key={`header-${index}`}
style={[
styles.customHeaderCell,
{ width: columnWidths[index] },
index === 0 && styles.headerCellFirst,
index === dataTitle.length - 1 && styles.headerCellLast,
]}
>
<Text style={styles.headerText} numberOfLines={2}>
{title}
</Text>
</View>
))}
</View>
);
};
return (
<SafeAreaView style={styles.container}>
<Header
isBack
title={'Thông tin điểm danh lớp ADCV'}
/>
<View style={styles.container}>
<ScrollView
style={styles.outerScrollView}
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.outerScrollContent}
>
<View style={styles.tableContainer}>
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
style={styles.horizontalScroll}
contentContainerStyle={styles.scrollContent}
nestedScrollEnabled={true}
>
<View style={[styles.tableWrapper, { width: totalTableWidth }]}>
{renderCustomHeader()}
<View style={styles.tableBody}>
{dataListInfo.map((rowData, rowIndex) => (
<View
key={`row-${rowIndex}`}
style={styles.customTableRow}
>
{rowData.map((cellData, cellIndex) => (
<View
key={`cell-${rowIndex}-${cellIndex}`}
style={[
styles.customTableCell,
{ width: columnWidths[cellIndex] },
cellIndex < rowData.length - 1 && styles.cellBorderRight,
rowIndex < dataListInfo.length - 1 && styles.cellBorderBottom,
]}
>
<Text
style={[styles.cellText, cellData.style]}
numberOfLines={2}
ellipsizeMode="tail"
>
{cellData.content}
</Text>
</View>
))}
</View>
))}
</View>
</View>
</ScrollView>
</View>
</ScrollView>
</View>
</SafeAreaView>
);
};
export default DetailRollCallView;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: R.colors.white,
},
outerScrollView: {
flex: 1,
},
outerScrollContent: {
flexGrow: 1,
},
tableContainer: {
paddingVertical: 10,
},
horizontalScroll: {
},
scrollContent: {
paddingHorizontal: 15,
},
tableWrapper: {
borderWidth: 1,
borderColor: R.colors.grey_200,
overflow: 'hidden',
},
customHeaderRow: {
flexDirection: 'row',
backgroundColor: R.colors.grey_200,
minHeight: 50,
},
customHeaderCell: {
justifyContent: 'center',
alignItems: 'center',
paddingHorizontal: 4,
borderRightWidth: 1,
borderRightColor: R.colors.grey_200,
},
headerCellFirst: {
borderTopLeftRadius: 7,
},
headerCellLast: {
borderTopRightRadius: 7,
borderRightWidth: 0,
},
tableBody: {
backgroundColor: R.colors.white,
},
customTableRow: {
flexDirection: 'row',
minHeight: 45,
},
customTableCell: {
justifyContent: 'center',
alignItems: 'center',
paddingHorizontal: 4,
backgroundColor: R.colors.white,
},
cellBorderRight: {
borderRightWidth: 1,
borderRightColor: R.colors.grey_200,
},
cellBorderBottom: {
borderBottomWidth: 1,
borderBottomColor: R.colors.grey_200,
},
headerText: {
fontFamily: R.fonts.InterRegular,
fontSize: R.fontsize.fontsSize12,
fontWeight: '400',
color: R.colors.black,
textAlign: 'center',
},
cellText: {
fontFamily: R.fonts.InterRegular,
fontSize: R.fontsize.fontsSize10,
fontWeight: '400',
color: R.colors.black,
textAlign: 'center',
},
});
\ No newline at end of file
import React, { useState } from 'react';
import {Text, View} from 'react-native';
import RollCallView from './view';
const RollCall = (props) =>{
const [dataRollCall, setDataRollCall] = useState([
{
id: 1, // FEATURE: Thêm unique ID
class_code: 'ADCV126',
subject_name: 'An ninh mạng',
total_lessons: '17/17',
absent_count: '1/17(10%)'
},
{
id: 2, // FEATURE: Thêm unique ID
class_code: 'ADCV127',
subject_name: 'Lập trình di động',
total_lessons: '15/17',
absent_count: '2/17(15%)'
},
{
id: 3, // FEATURE: Thêm unique ID
class_code: 'ADCV128',
subject_name: 'Cơ sở dữ liệu',
total_lessons: '16/17',
absent_count: '1/17(8%)'
},
{
id: 4, // FEATURE: Thêm unique ID
class_code: 'ADCV129',
subject_name: 'Cơ sở dữ liệu',
total_lessons: '16/17',
absent_count: '1/17(8%)'
},
{
id: 5, // FEATURE: Thêm unique ID
class_code: 'ADCV130',
subject_name: 'Cơ sở dữ liệu',
total_lessons: '16/17',
absent_count: '1/17(8%)'
},
{
id: 6, // FEATURE: Thêm unique ID
class_code: 'ADCV131',
subject_name: 'Cơ sở dữ liệu',
total_lessons: '16/17',
absent_count: '1/17(8%)'
},
{
id: 7, // FEATURE: Thêm unique ID
class_code: 'ADCV132',
subject_name: 'Cơ sở dữ liệu',
total_lessons: '16/17',
absent_count: '1/17(8%)'
},
{
id: 8, // FEATURE: Thêm unique ID
class_code: 'ADCV133',
subject_name: 'Cơ sở dữ liệu',
total_lessons: '16/17',
absent_count: '1/17(8%)'
}
]);
return(
<RollCallView
dataRollCall={dataRollCall}
/>
)
};
export default RollCall;
import { useNavigation } from "@react-navigation/native";
import React from "react";
import { View, Text, StyleSheet, Image, TouchableOpacity } from "react-native";
import { DETAILROLLCALL } from "../../routers/ScreenNames";
import R from "../../assets/R";
const ItemList = ({ item }) => {
const navigate = useNavigation();
const IconNext = R.images.icArrowRight;
return (
<TouchableOpacity
onPress={() => navigate.navigate(DETAILROLLCALL, { data: item })}
style={styles.card_item}
>
<View style={styles.content}>
<Text style={styles.text}>Mã lp:{item.class_code}</Text>
<Text style={styles.text}>Tên môn: {item.subject_name}</Text>
<View style={styles.footer}>
<Text style={styles.text}>Tng tiết: {item.total_lessons}</Text>
<Text style={styles.text}>S bui vng:{item.absent_count}</Text>
</View>
</View>
<IconNext width={15} height={15} />
</TouchableOpacity>
);
};
export default ItemList;
const styles = StyleSheet.create({
card_item: {
backgroundColor: R.colors.white,
marginHorizontal: 15,
marginVertical: 5,
paddingHorizontal: 15,
borderRadius: 10,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
shadowColor: R.colors.black,
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 1,
shadowRadius: 1,
elevation: 2,
},
content: {
flex: 1,
paddingVertical:10
},
footer: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginRight:15
},
text:{
fontSize: R.fontsize.fontsSize12,
fontWeight: '400',
fontFamily: R.fonts.InterRegular,
color: R.colors.black,
},
});
import React from "react";
import { Text, View, TouchableOpacity,FlatList, StyleSheet } from "react-native";
import R from "../../assets/R";
const ItemCalendar = ({ item }) => {
return (
<TouchableOpacity style={styles.container_schedule}>
<View style={styles.container_schedule_item}>
<Text style={styles.text_title}>Hc k 1 năm 2025</Text>
</View>
</TouchableOpacity>
);
};
export default ItemCalendar;
const styles = StyleSheet.create({
container_schedule: {
backgroundColor: R.colors.white,
marginHorizontal: 15,
marginVertical:15,
paddingHorizontal: 7,
paddingVertical: 5,
shadowColor: R.colors.black,
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.22,
shadowRadius: 2.22,
elevation: 3,
borderRadius: 10,
},
container_schedule_item: {
borderColor:R.colors.grey,
borderWidth: 1,
minHeight: 34,
flexDirection: "row",
borderRadius: 10,
alignItems: "center",
justifyContent: "center",
},
text_title:{
fontSize: R.fontsize.fontsSize14,
fontWeight: "400",
fontFamily: R.fonts.InterRegular,
color: R.colors.black,
}
});
import React from "react";
import {
View,
FlatList,
SafeAreaView,
StyleSheet,
} from "react-native";
import ItemList from "./item";
import Header from "../../components/Header/Header";
import I18n from "../../helper/i18/i18n";
import R from "../../assets/R";
import ItemCalendar from "./item_calendar";
const RollCallView = (props) => {
const { dataRollCall } = props;
const renderCardItem = ({ item }) => {
return <ItemList item={item} />;
};
return (
<SafeAreaView style={styles.container}>
<Header isBack title={I18n.t("RollCall")} />
<View style={styles.container}>
<ItemCalendar />
<View style={styles.flatlist}>
<FlatList
data={dataRollCall}
renderItem={renderCardItem}
keyExtractor={(item) => item.id.toString()}
scrollEnabled={true}
showsVerticalScrollIndicator={false}
/>
</View>
</View>
</SafeAreaView>
);
};
export default RollCallView;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: R.colors.white,
},
flatlist: {
flex: 1,
backgroundColor:R.colors.white,
},
});
import React, { useState } from 'react';
import {Text, View, StyleSheet} from 'react-native';
import TrainingPointView from './view';
const TrainingPoint = (props) => {
const [dataListInfo,setDataListInfo] = useState([
{semester:'1', school_year:'2024',activity_class:"PORP1", point_training:'100',classification:'Xuất sắc',note_training:''},
{semester:'2', school_year:'2024',activity_class:"PORP1", point_training:'100',classification:'Xuất sắc',note_training:''},
{semester:'3', school_year:'2024',activity_class:"PORP1", point_training:'100',classification:'Xuất sắc',note_training:''},
{semester:'4', school_year:'2024',activity_class:"PORP1", point_training:'100',classification:'Xuất sắc',note_training:''},
{semester:'5', school_year:'2024',activity_class:"PORP1", point_training:'100',classification:'Xuất sắc',note_training:''},
{semester:'6', school_year:'2024',activity_class:"PORP1", point_training:'100',classification:'Xuất sắc',note_training:''},
{semester:'7', school_year:'2024',activity_class:"PORP1", point_training:'100',classification:'Xuất sắc',note_training:''},
{semester:'8', school_year:'2024',activity_class:"PORP1", point_training:'100',classification:'Xuất sắc',note_training:''},
{semester:'9', school_year:'2024',activity_class:"PORP1", point_training:'100',classification:'Xuất sắc',note_training:''},
{semester:'10', school_year:'2024',activity_class:"PORP1", point_training:'100',classification:'Xuất sắc',note_training:''},
{semester:'11', school_year:'2024',activity_class:"PORP1", point_training:'100',classification:'Xuất sắc',note_training:''},
{semester:'12', school_year:'2024',activity_class:"PORP1", point_training:'100',classification:'Xuất sắc',note_training:''},
{semester:'13', school_year:'2024',activity_class:"PORP1", point_training:'100',classification:'Xuất sắc',note_training:''},
{semester:'14', school_year:'2024',activity_class:"PORP1", point_training:'100',classification:'Xuất sắc',note_training:''},
{semester:'15', school_year:'2024',activity_class:"PORP1", point_training:'100',classification:'Xuất sắc',note_training:''},
{semester:'16', school_year:'2024',activity_class:"PORP1", point_training:'100',classification:'Xuất sắc',note_training:''},
{semester:'17', school_year:'2024',activity_class:"PORP1", point_training:'100',classification:'Xuất sắc',note_training:''},
{semester:'18', school_year:'2024',activity_class:"PORP1", point_training:'100',classification:'Xuất sắc',note_training:''},
{semester:'19', school_year:'2024',activity_class:"PORP1", point_training:'100',classification:'Xuất sắc',note_training:''},
{semester:'20', school_year:'2024',activity_class:"PORP1", point_training:'100',classification:'Xuất sắc',note_training:''},
])
const dataList = dataListInfo.map(item => [
item.semester,
item.school_year,
item.activity_class,
item.point_training,
item.classification,
item.note_training
])
const [dataTitle] = useState([
'Học kì', 'Năm học', 'Lớp sinh hoạt', 'Điểm rèn luyện', 'Xếp loại', 'Ghi chú'
])
return (
<TrainingPointView
dataTitle={dataTitle}
dataListInfo={dataList}
/>
);
};
export default TrainingPoint;
import { StyleSheet } from "react-native";
import R from "../../assets/R";
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor:R.colors.white,
},
card_container: {
backgroundColor: R.colors.white,
marginHorizontal: 15,
marginVertical: 10,
shadowColor: R.colors.black,
shadowOffset: {
width: 0,
height: 0,
},
shadowOpacity: 1,
shadowRadius: 5,
elevation: 5,
paddingVertical: 10,
paddingHorizontal: 15,
borderRadius: 10,
},
table: {
borderWidth: 1,
borderColor: R.colors.grey_200,
},
head: {
backgroundColor: R.colors.grey_200,
minHeight: 44,
},
row: {
flexDirection: 'row',
backgroundColor: R.colors.white,
},
cell: {
justifyContent: 'center',
alignItems: 'center',
paddingVertical: 10
},
text_header: {
fontFamily: R.fonts.InterRegular,
fontSize: R.fontsize.fontsSize12,
fontWeight: '400',
color: R.colors.black,
textAlign: 'center',
},
text_content: {
fontFamily: R.fonts.InterRegular,
fontSize: R.fontsize.fontsSize12,
fontWeight: '400',
color: R.colors.black,
textAlign: 'center',
},
text_title: {
fontFamily: R.fonts.InterSemiBold,
fontSize: R.fontsize.fontsSize14,
fontWeight: '500',
color: R.colors.blue500,
marginBottom: 10
}
});
export default styles;
\ No newline at end of file
import React from "react";
import {
Text,
View,
TouchableOpacity,
StyleSheet,
SafeAreaView,
ScrollView,
} from "react-native";
import {
Table,
TableWrapper,
Row,
Cell,
} from 'react-native-reanimated-table';
import Header from "../../components/Header/Header";
import R from "../../assets/R";
import I18n from "../../helper/i18/i18n";
import styles from "./style";
const TrainingPointView = (props) => {
const { dataTitle, dataListInfo } = props;
const columnFlex = [1, 2, 2, 2, 2, 1];
return (
<SafeAreaView style={styles.container}>
<Header isBack title={I18n.t("TrainingPoint")} />
<ScrollView
showsVerticalScrollIndicator={false}
>
<View style={styles.container}>
<View style={styles.card_container}>
<Text style={styles.text_title}>Đim rèn luyn trung bình: 100</Text>
<Table borderStyle={styles.table}>
<Row
data={dataTitle}
style={styles.head}
flexArr={columnFlex}
textStyle={styles.text_header}
/>
{dataListInfo.map((rowData, rowIndex) => (
<TableWrapper key={rowIndex} style={styles.row}>
{rowData.map((cellData, cellIndex) => (
<Cell
key={cellIndex}
flex={columnFlex[cellIndex]}
data={
<Text
style={[styles.text_content, cellData.style]}
numberOfLines={1}
ellipsizeMode="tail"
>
{cellData}
</Text>
}
style={styles.cell}
/>
))}
</TableWrapper>
))}
</Table>
</View>
</View>
</ScrollView>
</SafeAreaView>
);
};
export default TrainingPointView;
import React, { useMemo, useState } from "react";
import { Text, View, StyleSheet } from "react-native";
import TrainingProgramView from "./view";
import R from "../../assets/R";
const TrainingProgram = (props) => {
const [dataTrainingProgram, setDataTrainingProgram] = useState([
{
id: "1",
type: "title",
title: "1. GIỚI THIỆU CHUNG",
},
{
id: "1.1",
type: "subTitle",
title: "1.1. Mục tiêu đào tạo",
content:
"Chương trình Cử nhân Công nghệ Thông tin đào tạo những cử nhân ngành Công nghệ thông tin có phẩm chất chính trị tốt, có đạo đức nghề nghiệp, có ý thức trách nhiệm tổ chức, và có sức khỏe tốt; nắm vững các kiến thức cơ bản và chuyên môn sâu về công nghệ thông tin (CNTT); đáp ứng các yêu cầu về nghiên cứu phát triển và ứng dụng công nghệ thông tin của xã hội; có năng lực tham mưu, tư vấn và có khả năng tổ chức thực hiện nhiệm vụ với tư cách của một chuyên viên trong lĩnh vực CNTT. Bên cạnh đó, trên cơ sở các kiến thức được trang bị ở trình độ đại học, người học có đủ năng lực từng bước hoàn thiện khả năng độc lập nghiên cứu, tự bồi dưỡng và tiếp tục lên học các trình độ cao hơn.",
},
{
id: "1.2",
type: "subTitle",
title: "1.2. Vị trí và khả năng làm việc sau tốt nghiệp",
content: [
"Sinh viên tốt nghiệp Chương trình đào tạo Cử nhân Công nghệ Thông tin có khả năng làm việc ở những phạm vi và lĩnh vực khác nhau như:",
"1) Chuyên viên thiết kế, xây dựng và quản lý các dự án nghiên cứu và ứng dụng CNTT, chủ yếu trong lĩnh vực: giao thông, xây dựng, địa lý, môi trường, viễn thám.",
"2) Chuyên viên quản lý, giám sát, đầu tư các dự án công nghệ thông tin.",
"3) Chuyên viên khai thác dữ liệu và thông tin ứng dụng cho các doanh nghiệp trong vấn đề phân tích định lượng.",
"4) Chuyên viên có kĩ năng phát triển các ứng dụng truyền thông xã hội và công nghệ Web.",
"5) Cán bộ giảng dạy, nghiên cứu khoa học và ứng dụng CNTT ở các trường đại học và cao đẳng trên cả nước.",
],
},
{
id: "1.3",
type: "subTitle",
title: "1.3. Quan điểm xây dựng chương trình đào tạo",
content: [
"Chương trình được thiết kế sao cho đảm bảo đủ độ phủ, độ sâu nhất định nhằm tạo điều kiện, cơ hội phát triển cho sinh viên làm việc, và có thể tiếp tục nghiên cứu chuyên sâu về các chuyên ngành CNTT, trong đó độ phủ được đặt trọng tâm.",
"Chương trình được thiết kế với mục tiêu phát triển nguồn nhân lực phục vụ cho công cuộc phát triển và hội nhập của đất nước:",
"- Đào tạo nguồn nhân lực có khả năng để vận hành, quản lý, giám sát; phân tích và phát triển các ứng dụng công nghệ thông tin tại các doanh nghiệp, các đơn vị không chuyên về CNTT nhằm tạo ra các giá trị lợi ích gia tăng cho các doanh nghiệp;",
"- Đào tạo nguồn nhân lực có khả năng khai thác dữ liệu và thông tin ứng dụng cho các doanh nghiệp trong vấn đề phân tích định lượng;",
"- Đào tạo nguồn nhân lực có kĩ năng phát triển ứng dụng truyền thông xã hội và công nghệ Web;",
"- Đào tạo nguồn nhân lực kỹ thuật tham gia các quy trình thiết kế, xây dựng, quản lý các dự án nghiên cứu và ứng dụng CNTT, chủ yếu trong lĩnh vực: địa lý, môi trường, viễn thám.",
"Chương trình được thiết kế, xây dựng dựa vào tầm nhìn và sứ mệnh nhà trường; phiếu góp ý của doanh nghiệp, sinh viên tốt nghiệp, giảng viên giảng dạy và tài liệu tham khảo chính là Chương trình đào tạo Đại học về Công nghệ Thông tin của ACM (Association for Computing Machinery) và IEEE Computer Society ấn hành.",
],
},
]);
const hasContent = (item) => {
if (!item.content) return false;
if (Array.isArray(item.content)) {
return item.content.length > 0;
}
return item.content.trim().length > 0;
};
const processItemContent = (item) => {
if (!hasContent(item)) return null;
if (Array.isArray(item.content)) {
return item.content.map((line, index) => (
<Text
key={`${item.id}-content-${index}`}
style={styles.listItem}
>
{line}
</Text>
));
} else {
return (
<Text style={styles.paragraph}>
{item.content}
</Text>
);
}
};
const enhancedDataTrainingProgram = useMemo(() => {
return dataTrainingProgram.map(item => ({
...item,
hasContentFlag: hasContent(item),
processedContent: processItemContent(item)
}));
}, [dataTrainingProgram]);
return <TrainingProgramView dataTrainingProgram={enhancedDataTrainingProgram} />;
};
export default TrainingProgram;
const styles = {
paragraph: {
fontSize: R.fontsize.fontSizeContent,
color: R.colors.black,
textAlign: "justify",
fontFamily: R.fonts.InterSemiBold,
},
listItem: {
fontSize: R.fontsize.fontSizeContent,
lineHeight: 20,
color: R.colors.black,
fontFamily: R.fonts.InterSemiBold,
},
};
\ No newline at end of file
import React from "react";
import { Text, View, StyleSheet } from "react-native";
import R from "../../assets/R";
const ItemView = ({ item, hasContentFlag, processedContent}) => {
return (
<View style={styles.container}>
{item.type === "title" && (
<Text style={styles.titleHeader}>{item.title}</Text>
)}
{item.type === "subTitle" && (
<Text style={styles.subTitle}>{item.title}</Text>
)}
{hasContentFlag && processedContent}
</View>
);
};
export default ItemView;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor:R.colors.white,
paddingHorizontal: 15,
},
titleHeader: {
fontSize: R.fontsize.fontsSize14,
fontWeight: "bold",
color: R.colors.blue500,
fontFamily:R.fonts.InterSemiBold,
},
subTitle: {
fontSize: R.fontsize.fontsSize14,
fontWeight: "500",
fontFamily:R.fonts.InterSemiBold,
color: R.colors.blue500,
},
paragraph: {
fontSize: R.fontsize.fontsSize10,
color: R.colors.blue500,
textAlign: "justify",
fontFamily:R.fonts.InterSemiBold
},
listItem: {
fontSize: R.fontsize.fontsSize10,
lineHeight: 20,
color: R.colors.blue500,
fontFamily:R.fonts.InterSemiBold
},
});
import React from "react";
import {
View,
StyleSheet,
FlatList,
SafeAreaView,
} from "react-native";
import R from "../../assets/R";
import Header from "../../components/Header/Header";
import ItemView from "./item_view";
const TrainingProgramView = (props) => {
const { dataTrainingProgram} = props;
const renderContent = ({ item }) => {
return (
<ItemView
item={item}
hasContentFlag={item.hasContentFlag}
processedContent={item.processedContent}
/>
);
};
return (
<SafeAreaView style={styles.container}>
<Header isBack title={"Chương trình đào tạo"} />
<View style={styles.container}>
<View>
<FlatList
data={dataTrainingProgram}
renderItem={renderContent}
keyExtractor={(item) => item.id}
showsVerticalScrollIndicator={false}
removeClippedSubviews={true}
maxToRenderPerBatch={10}
windowSize={10}
/>
</View>
</View>
</SafeAreaView>
);
};
export default TrainingProgramView;
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor:R.colors.white
},
});
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.28.0.tgz#9fc6fd58c2a6a15243cd13983224968392070790" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.28.0.tgz#9fc6fd58c2a6a15243cd13983224968392070790"
integrity sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw== integrity sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==
"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.13.16", "@babel/core@^7.20.0", "@babel/core@^7.23.9": "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.13.16", "@babel/core@^7.20.0", "@babel/core@^7.21.3", "@babel/core@^7.23.9":
version "7.28.0" version "7.28.0"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.28.0.tgz#55dad808d5bf3445a108eefc88ea3fdf034749a4" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.28.0.tgz#55dad808d5bf3445a108eefc88ea3fdf034749a4"
integrity sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ== integrity sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==
...@@ -545,7 +545,7 @@ ...@@ -545,7 +545,7 @@
dependencies: dependencies:
"@babel/helper-plugin-utils" "^7.27.1" "@babel/helper-plugin-utils" "^7.27.1"
"@babel/plugin-transform-class-properties@^7.0.0-0", "@babel/plugin-transform-class-properties@^7.27.1": "@babel/plugin-transform-class-properties@^7.27.1":
version "7.27.1" version "7.27.1"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz#dd40a6a370dfd49d32362ae206ddaf2bb082a925" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz#dd40a6a370dfd49d32362ae206ddaf2bb082a925"
integrity sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA== integrity sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==
...@@ -561,7 +561,7 @@ ...@@ -561,7 +561,7 @@
"@babel/helper-create-class-features-plugin" "^7.27.1" "@babel/helper-create-class-features-plugin" "^7.27.1"
"@babel/helper-plugin-utils" "^7.27.1" "@babel/helper-plugin-utils" "^7.27.1"
"@babel/plugin-transform-classes@^7.0.0", "@babel/plugin-transform-classes@^7.0.0-0", "@babel/plugin-transform-classes@^7.28.0": "@babel/plugin-transform-classes@^7.0.0", "@babel/plugin-transform-classes@^7.28.0":
version "7.28.0" version "7.28.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.0.tgz#12fa46cffc32a6e084011b650539e880add8a0f8" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.0.tgz#12fa46cffc32a6e084011b650539e880add8a0f8"
integrity sha512-IjM1IoJNw72AZFlj33Cu8X0q2XK/6AaVC3jQu+cgQ5lThWD5ajnuUAml80dqRmOhmPkTH8uAwnpMu9Rvj0LTRA== integrity sha512-IjM1IoJNw72AZFlj33Cu8X0q2XK/6AaVC3jQu+cgQ5lThWD5ajnuUAml80dqRmOhmPkTH8uAwnpMu9Rvj0LTRA==
...@@ -950,7 +950,7 @@ ...@@ -950,7 +950,7 @@
"@babel/helper-create-regexp-features-plugin" "^7.27.1" "@babel/helper-create-regexp-features-plugin" "^7.27.1"
"@babel/helper-plugin-utils" "^7.27.1" "@babel/helper-plugin-utils" "^7.27.1"
"@babel/plugin-transform-unicode-regex@^7.0.0", "@babel/plugin-transform-unicode-regex@^7.0.0-0", "@babel/plugin-transform-unicode-regex@^7.27.1": "@babel/plugin-transform-unicode-regex@^7.0.0", "@babel/plugin-transform-unicode-regex@^7.27.1":
version "7.27.1" version "7.27.1"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz#25948f5c395db15f609028e370667ed8bae9af97" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz#25948f5c395db15f609028e370667ed8bae9af97"
integrity sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw== integrity sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==
...@@ -1109,7 +1109,7 @@ ...@@ -1109,7 +1109,7 @@
"@babel/types" "^7.28.0" "@babel/types" "^7.28.0"
debug "^4.3.1" debug "^4.3.1"
"@babel/types@^7.0.0", "@babel/types@^7.20.0", "@babel/types@^7.20.7", "@babel/types@^7.24.7", "@babel/types@^7.27.1", "@babel/types@^7.27.3", "@babel/types@^7.28.0", "@babel/types@^7.28.2", "@babel/types@^7.3.3", "@babel/types@^7.4.4": "@babel/types@^7.0.0", "@babel/types@^7.20.0", "@babel/types@^7.20.7", "@babel/types@^7.21.3", "@babel/types@^7.24.7", "@babel/types@^7.27.1", "@babel/types@^7.27.3", "@babel/types@^7.28.0", "@babel/types@^7.28.2", "@babel/types@^7.3.3", "@babel/types@^7.4.4":
version "7.28.2" version "7.28.2"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.2.tgz#da9db0856a9a88e0a13b019881d7513588cf712b" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.28.2.tgz#da9db0856a9a88e0a13b019881d7513588cf712b"
integrity sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ== integrity sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==
...@@ -1531,11 +1531,6 @@ ...@@ -1531,11 +1531,6 @@
dependencies: dependencies:
deep-assign "^3.0.0" deep-assign "^3.0.0"
"@react-native-community/checkbox@^0.5.9":
version "0.5.20"
resolved "https://registry.yarnpkg.com/@react-native-community/checkbox/-/checkbox-0.5.20.tgz#7b8004a80a90988d5dff208693524f069270b849"
integrity sha512-NuTAOFmttPEX7cwDbVr5Rf0HR86zCzX2FllebMayxca//dFH6X5DLySftra/PC2K9kt42yashWK7T2Tucm5JTQ==
"@react-native-community/cli-clean@13.6.8": "@react-native-community/cli-clean@13.6.8":
version "13.6.8" version "13.6.8"
resolved "https://registry.yarnpkg.com/@react-native-community/cli-clean/-/cli-clean-13.6.8.tgz#95ce964047f005152ac100394b6dcd5d2cc2a474" resolved "https://registry.yarnpkg.com/@react-native-community/cli-clean/-/cli-clean-13.6.8.tgz#95ce964047f005152ac100394b6dcd5d2cc2a474"
...@@ -1920,6 +1915,15 @@ ...@@ -1920,6 +1915,15 @@
react-is "^16.13.0" react-is "^16.13.0"
use-latest-callback "^0.2.1" use-latest-callback "^0.2.1"
"@react-navigation/drawer@^6.x":
version "6.7.2"
resolved "https://registry.yarnpkg.com/@react-navigation/drawer/-/drawer-6.7.2.tgz#3c85298c73af915f3cd5e6ca26a1a26d61010256"
integrity sha512-o4g2zgTZa2+oLd+8V33etrSM38KIqu8S/zCBTsdsHUoQyVE7JNRiv3Qgq/jMvEb8PZCqWmm7jHItcgzrBuwyOQ==
dependencies:
"@react-navigation/elements" "^1.3.31"
color "^4.2.3"
warn-once "^0.1.0"
"@react-navigation/elements@^1.3.31": "@react-navigation/elements@^1.3.31":
version "1.3.31" version "1.3.31"
resolved "https://registry.yarnpkg.com/@react-navigation/elements/-/elements-1.3.31.tgz#28dd802a0787bb03fc0e5be296daf1804dbebbcf" resolved "https://registry.yarnpkg.com/@react-navigation/elements/-/elements-1.3.31.tgz#28dd802a0787bb03fc0e5be296daf1804dbebbcf"
...@@ -2050,6 +2054,103 @@ ...@@ -2050,6 +2054,103 @@
dependencies: dependencies:
"@sinonjs/commons" "^3.0.0" "@sinonjs/commons" "^3.0.0"
"@svgr/babel-plugin-add-jsx-attribute@8.0.0":
version "8.0.0"
resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz#4001f5d5dd87fa13303e36ee106e3ff3a7eb8b22"
integrity sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==
"@svgr/babel-plugin-remove-jsx-attribute@8.0.0":
version "8.0.0"
resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz#69177f7937233caca3a1afb051906698f2f59186"
integrity sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==
"@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0":
version "8.0.0"
resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz#c2c48104cfd7dcd557f373b70a56e9e3bdae1d44"
integrity sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==
"@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0":
version "8.0.0"
resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz#8fbb6b2e91fa26ac5d4aa25c6b6e4f20f9c0ae27"
integrity sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==
"@svgr/babel-plugin-svg-dynamic-title@8.0.0":
version "8.0.0"
resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz#1d5ba1d281363fc0f2f29a60d6d936f9bbc657b0"
integrity sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==
"@svgr/babel-plugin-svg-em-dimensions@8.0.0":
version "8.0.0"
resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz#35e08df300ea8b1d41cb8f62309c241b0369e501"
integrity sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==
"@svgr/babel-plugin-transform-react-native-svg@8.1.0":
version "8.1.0"
resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz#90a8b63998b688b284f255c6a5248abd5b28d754"
integrity sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==
"@svgr/babel-plugin-transform-svg-component@8.0.0":
version "8.0.0"
resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz#013b4bfca88779711f0ed2739f3f7efcefcf4f7e"
integrity sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==
"@svgr/babel-preset@8.1.0":
version "8.1.0"
resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-8.1.0.tgz#0e87119aecdf1c424840b9d4565b7137cabf9ece"
integrity sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==
dependencies:
"@svgr/babel-plugin-add-jsx-attribute" "8.0.0"
"@svgr/babel-plugin-remove-jsx-attribute" "8.0.0"
"@svgr/babel-plugin-remove-jsx-empty-expression" "8.0.0"
"@svgr/babel-plugin-replace-jsx-attribute-value" "8.0.0"
"@svgr/babel-plugin-svg-dynamic-title" "8.0.0"
"@svgr/babel-plugin-svg-em-dimensions" "8.0.0"
"@svgr/babel-plugin-transform-react-native-svg" "8.1.0"
"@svgr/babel-plugin-transform-svg-component" "8.0.0"
"@svgr/core@^8.1.0":
version "8.1.0"
resolved "https://registry.yarnpkg.com/@svgr/core/-/core-8.1.0.tgz#41146f9b40b1a10beaf5cc4f361a16a3c1885e88"
integrity sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==
dependencies:
"@babel/core" "^7.21.3"
"@svgr/babel-preset" "8.1.0"
camelcase "^6.2.0"
cosmiconfig "^8.1.3"
snake-case "^3.0.4"
"@svgr/hast-util-to-babel-ast@8.0.0":
version "8.0.0"
resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz#6952fd9ce0f470e1aded293b792a2705faf4ffd4"
integrity sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==
dependencies:
"@babel/types" "^7.21.3"
entities "^4.4.0"
"@svgr/plugin-jsx@^8.1.0":
version "8.1.0"
resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz#96969f04a24b58b174ee4cd974c60475acbd6928"
integrity sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==
dependencies:
"@babel/core" "^7.21.3"
"@svgr/babel-preset" "8.1.0"
"@svgr/hast-util-to-babel-ast" "8.0.0"
svg-parser "^2.0.4"
"@svgr/plugin-svgo@^8.1.0":
version "8.1.0"
resolved "https://registry.yarnpkg.com/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz#b115b7b967b564f89ac58feae89b88c3decd0f00"
integrity sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==
dependencies:
cosmiconfig "^8.1.3"
deepmerge "^4.3.1"
svgo "^3.0.2"
"@trysound/sax@0.2.0":
version "0.2.0"
resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad"
integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==
"@types/babel__core@^7.1.14": "@types/babel__core@^7.1.14":
version "7.20.5" version "7.20.5"
resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017"
...@@ -3078,6 +3179,11 @@ commander@^2.20.0, commander@^2.9.0: ...@@ -3078,6 +3179,11 @@ commander@^2.20.0, commander@^2.9.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
commander@^7.2.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
commander@^9.4.1: commander@^9.4.1:
version "9.5.0" version "9.5.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30" resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30"
...@@ -3155,6 +3261,16 @@ cosmiconfig@^5.0.5, cosmiconfig@^5.1.0: ...@@ -3155,6 +3261,16 @@ cosmiconfig@^5.0.5, cosmiconfig@^5.1.0:
js-yaml "^3.13.1" js-yaml "^3.13.1"
parse-json "^4.0.0" parse-json "^4.0.0"
cosmiconfig@^8.1.3:
version "8.3.6"
resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.3.6.tgz#060a2b871d66dba6c8538ea1118ba1ac16f5fae3"
integrity sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==
dependencies:
import-fresh "^3.3.0"
js-yaml "^4.1.0"
parse-json "^5.2.0"
path-type "^4.0.0"
create-jest@^29.7.0: create-jest@^29.7.0:
version "29.7.0" version "29.7.0"
resolved "https://registry.yarnpkg.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320" resolved "https://registry.yarnpkg.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320"
...@@ -3199,6 +3315,17 @@ css-select@^2.1.0: ...@@ -3199,6 +3315,17 @@ css-select@^2.1.0:
domutils "^1.7.0" domutils "^1.7.0"
nth-check "^1.0.2" nth-check "^1.0.2"
css-select@^5.1.0:
version "5.2.2"
resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.2.2.tgz#01b6e8d163637bb2dd6c982ca4ed65863682786e"
integrity sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==
dependencies:
boolbase "^1.0.0"
css-what "^6.1.0"
domhandler "^5.0.2"
domutils "^3.0.1"
nth-check "^2.0.1"
css-select@~1.2.0: css-select@~1.2.0:
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858"
...@@ -3226,6 +3353,22 @@ css-tree@^1.0.0-alpha.39: ...@@ -3226,6 +3353,22 @@ css-tree@^1.0.0-alpha.39:
mdn-data "2.0.14" mdn-data "2.0.14"
source-map "^0.6.1" source-map "^0.6.1"
css-tree@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.3.1.tgz#10264ce1e5442e8572fc82fbe490644ff54b5c20"
integrity sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==
dependencies:
mdn-data "2.0.30"
source-map-js "^1.0.1"
css-tree@~2.2.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.2.1.tgz#36115d382d60afd271e377f9c5f67d02bd48c032"
integrity sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==
dependencies:
mdn-data "2.0.28"
source-map-js "^1.0.1"
css-what@2.1: css-what@2.1:
version "2.1.3" version "2.1.3"
resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2"
...@@ -3236,6 +3379,18 @@ css-what@^3.2.1: ...@@ -3236,6 +3379,18 @@ css-what@^3.2.1:
resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.4.2.tgz#ea7026fcb01777edbde52124e21f327e7ae950e4" resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.4.2.tgz#ea7026fcb01777edbde52124e21f327e7ae950e4"
integrity sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ== integrity sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==
css-what@^6.1.0:
version "6.2.2"
resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.2.2.tgz#cdcc8f9b6977719fdfbd1de7aec24abf756b9dea"
integrity sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==
csso@^5.0.5:
version "5.0.5"
resolved "https://registry.yarnpkg.com/csso/-/csso-5.0.5.tgz#f9b7fe6cc6ac0b7d90781bb16d5e9874303e2ca6"
integrity sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==
dependencies:
css-tree "~2.2.0"
csstype@^3.0.2, csstype@^3.0.8, csstype@^3.0.9: csstype@^3.0.2, csstype@^3.0.8, csstype@^3.0.9:
version "3.1.3" version "3.1.3"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81"
...@@ -3385,7 +3540,7 @@ deep-is@^0.1.3: ...@@ -3385,7 +3540,7 @@ deep-is@^0.1.3:
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
deepmerge@^4.2.2, deepmerge@^4.3.0: deepmerge@^4.2.2, deepmerge@^4.3.0, deepmerge@^4.3.1:
version "4.3.1" version "4.3.1"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a"
integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==
...@@ -3478,6 +3633,15 @@ dom-serializer@^1.0.1: ...@@ -3478,6 +3633,15 @@ dom-serializer@^1.0.1:
domhandler "^4.2.0" domhandler "^4.2.0"
entities "^2.0.0" entities "^2.0.0"
dom-serializer@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53"
integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==
dependencies:
domelementtype "^2.3.0"
domhandler "^5.0.2"
entities "^4.2.0"
dom-serializer@~0.1.0: dom-serializer@~0.1.0:
version "0.1.1" version "0.1.1"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0"
...@@ -3491,7 +3655,7 @@ domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1: ...@@ -3491,7 +3655,7 @@ domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1:
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f"
integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==
domelementtype@^2.0.1, domelementtype@^2.2.0: domelementtype@^2.0.1, domelementtype@^2.2.0, domelementtype@^2.3.0:
version "2.3.0" version "2.3.0"
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d"
integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
...@@ -3510,6 +3674,13 @@ domhandler@^4.2.0, domhandler@^4.2.2: ...@@ -3510,6 +3674,13 @@ domhandler@^4.2.0, domhandler@^4.2.2:
dependencies: dependencies:
domelementtype "^2.2.0" domelementtype "^2.2.0"
domhandler@^5.0.2, domhandler@^5.0.3:
version "5.0.3"
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31"
integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==
dependencies:
domelementtype "^2.3.0"
domutils@1.5.1: domutils@1.5.1:
version "1.5.1" version "1.5.1"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"
...@@ -3535,6 +3706,23 @@ domutils@^2.8.0: ...@@ -3535,6 +3706,23 @@ domutils@^2.8.0:
domelementtype "^2.2.0" domelementtype "^2.2.0"
domhandler "^4.2.0" domhandler "^4.2.0"
domutils@^3.0.1:
version "3.2.2"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.2.2.tgz#edbfe2b668b0c1d97c24baf0f1062b132221bc78"
integrity sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==
dependencies:
dom-serializer "^2.0.0"
domelementtype "^2.3.0"
domhandler "^5.0.3"
dot-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751"
integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==
dependencies:
no-case "^3.0.4"
tslib "^2.0.3"
dunder-proto@^1.0.0, dunder-proto@^1.0.1: dunder-proto@^1.0.0, dunder-proto@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a"
...@@ -3596,6 +3784,11 @@ entities@^3.0.1: ...@@ -3596,6 +3784,11 @@ entities@^3.0.1:
resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4" resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4"
integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q== integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==
entities@^4.2.0, entities@^4.4.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
envinfo@^7.10.0: envinfo@^7.10.0:
version "7.14.0" version "7.14.0"
resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.14.0.tgz#26dac5db54418f2a4c1159153a0b2ae980838aae" resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.14.0.tgz#26dac5db54418f2a4c1159153a0b2ae980838aae"
...@@ -4529,7 +4722,7 @@ import-fresh@^2.0.0: ...@@ -4529,7 +4722,7 @@ import-fresh@^2.0.0:
caller-path "^2.0.0" caller-path "^2.0.0"
resolve-from "^3.0.0" resolve-from "^3.0.0"
import-fresh@^3.2.1: import-fresh@^3.2.1, import-fresh@^3.3.0:
version "3.3.1" version "3.3.1"
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf"
integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==
...@@ -5638,6 +5831,13 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1, loose-envify@^1.4 ...@@ -5638,6 +5831,13 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1, loose-envify@^1.4
dependencies: dependencies:
js-tokens "^3.0.0 || ^4.0.0" js-tokens "^3.0.0 || ^4.0.0"
lower-case@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28"
integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==
dependencies:
tslib "^2.0.3"
lru-cache@^5.1.1: lru-cache@^5.1.1:
version "5.1.1" version "5.1.1"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
...@@ -5682,6 +5882,16 @@ mdn-data@2.0.14: ...@@ -5682,6 +5882,16 @@ mdn-data@2.0.14:
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==
mdn-data@2.0.28:
version "2.0.28"
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.28.tgz#5ec48e7bef120654539069e1ae4ddc81ca490eba"
integrity sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==
mdn-data@2.0.30:
version "2.0.30"
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc"
integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==
memoize-one@^5.0.0, memoize-one@^5.2.1: memoize-one@^5.0.0, memoize-one@^5.2.1:
version "5.2.1" version "5.2.1"
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e" resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e"
...@@ -6013,6 +6223,14 @@ neo-async@^2.5.0: ...@@ -6013,6 +6223,14 @@ neo-async@^2.5.0:
resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
no-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d"
integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==
dependencies:
lower-case "^2.0.2"
tslib "^2.0.3"
nocache@^3.0.1: nocache@^3.0.1:
version "3.0.4" version "3.0.4"
resolved "https://registry.yarnpkg.com/nocache/-/nocache-3.0.4.tgz#5b37a56ec6e09fc7d401dceaed2eab40c8bfdf79" resolved "https://registry.yarnpkg.com/nocache/-/nocache-3.0.4.tgz#5b37a56ec6e09fc7d401dceaed2eab40c8bfdf79"
...@@ -6101,6 +6319,13 @@ nth-check@^1.0.2, nth-check@~1.0.1: ...@@ -6101,6 +6319,13 @@ nth-check@^1.0.2, nth-check@~1.0.1:
dependencies: dependencies:
boolbase "~1.0.0" boolbase "~1.0.0"
nth-check@^2.0.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d"
integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==
dependencies:
boolbase "^1.0.0"
nullthrows@^1.1.1: nullthrows@^1.1.1:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1" resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1"
...@@ -6329,6 +6554,11 @@ parseurl@~1.3.3: ...@@ -6329,6 +6554,11 @@ parseurl@~1.3.3:
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
path-dirname@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0"
integrity sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==
path-exists@^3.0.0: path-exists@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
...@@ -6359,7 +6589,7 @@ path-type@^4.0.0: ...@@ -6359,7 +6589,7 @@ path-type@^4.0.0:
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
picocolors@^1.1.1: picocolors@^1.0.0, picocolors@^1.1.1:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
...@@ -6676,11 +6906,6 @@ react-native-iphone-x-helper@^1.3.1: ...@@ -6676,11 +6906,6 @@ react-native-iphone-x-helper@^1.3.1:
resolved "https://registry.yarnpkg.com/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.3.1.tgz#20c603e9a0e765fd6f97396638bdeb0e5a60b010" resolved "https://registry.yarnpkg.com/react-native-iphone-x-helper/-/react-native-iphone-x-helper-1.3.1.tgz#20c603e9a0e765fd6f97396638bdeb0e5a60b010"
integrity sha512-HOf0jzRnq2/aFUcdCJ9w9JGzN3gdEg0zFE4FyYlp4jtidqU03D5X7ZegGKfT1EWteR0gPBGp9ye5T5FvSWi9Yg== integrity sha512-HOf0jzRnq2/aFUcdCJ9w9JGzN3gdEg0zFE4FyYlp4jtidqU03D5X7ZegGKfT1EWteR0gPBGp9ye5T5FvSWi9Yg==
react-native-is-edge-to-edge@1.1.7:
version "1.1.7"
resolved "https://registry.yarnpkg.com/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.1.7.tgz#28947688f9fafd584e73a4f935ea9603bd9b1939"
integrity sha512-EH6i7E8epJGIcu7KpfXYXiV2JFIYITtq+rVS8uEb+92naMRBdxhTuS8Wn2Q7j9sqyO0B+Xbaaf9VdipIAmGW4w==
react-native-linear-gradient@^2.6.2: react-native-linear-gradient@^2.6.2:
version "2.8.3" version "2.8.3"
resolved "https://registry.yarnpkg.com/react-native-linear-gradient/-/react-native-linear-gradient-2.8.3.tgz#9a116649f86d74747304ee13db325e20b21e564f" resolved "https://registry.yarnpkg.com/react-native-linear-gradient/-/react-native-linear-gradient-2.8.3.tgz#9a116649f86d74747304ee13db325e20b21e564f"
...@@ -6746,23 +6971,24 @@ react-native-ratings@8.0.4: ...@@ -6746,23 +6971,24 @@ react-native-ratings@8.0.4:
dependencies: dependencies:
lodash "^4.17.15" lodash "^4.17.15"
react-native-reanimated@^3.12.1: react-native-reanimated-table@^0.0.2:
version "3.19.1" version "0.0.2"
resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-3.19.1.tgz#761f731bfb5dc67724ca7bf7bce90a6da975d753" resolved "https://registry.yarnpkg.com/react-native-reanimated-table/-/react-native-reanimated-table-0.0.2.tgz#015392dbc12fb03fd872d5a7eea9bc84c4f3a685"
integrity sha512-ILL0FSNzSVIg6WuawrsMBvNxk2yJFiTUcahimXDAeNiE/09eagVUlHhYWXAAmH0umvAOafBaGjO7YfBhUrf5ZQ== integrity sha512-OeuqfU1AFEmHNTJlEOLWrV78JgAXnM0/ZrCm0Ab+9e5nwYJ+xab/UFXkNKz3Gyf08ZfLSNzwMQRjt3eZWPWoGA==
react-native-reanimated@3.12.1:
version "3.12.1"
resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-3.12.1.tgz#301560b35cb224c30d6c7b3e4e260cecb94035a0"
integrity sha512-aXyV1ydKNA2u9fqRL8Z4fJ2RxNAusujNDdC4k0y9CawNEay5AGYgxhANqmjAabGRzHxsvfCXJC09lvbTRMHIFA==
dependencies: dependencies:
"@babel/plugin-transform-arrow-functions" "^7.0.0-0" "@babel/plugin-transform-arrow-functions" "^7.0.0-0"
"@babel/plugin-transform-class-properties" "^7.0.0-0"
"@babel/plugin-transform-classes" "^7.0.0-0"
"@babel/plugin-transform-nullish-coalescing-operator" "^7.0.0-0" "@babel/plugin-transform-nullish-coalescing-operator" "^7.0.0-0"
"@babel/plugin-transform-optional-chaining" "^7.0.0-0" "@babel/plugin-transform-optional-chaining" "^7.0.0-0"
"@babel/plugin-transform-shorthand-properties" "^7.0.0-0" "@babel/plugin-transform-shorthand-properties" "^7.0.0-0"
"@babel/plugin-transform-template-literals" "^7.0.0-0" "@babel/plugin-transform-template-literals" "^7.0.0-0"
"@babel/plugin-transform-unicode-regex" "^7.0.0-0"
"@babel/preset-typescript" "^7.16.7" "@babel/preset-typescript" "^7.16.7"
convert-source-map "^2.0.0" convert-source-map "^2.0.0"
invariant "^2.2.4" invariant "^2.2.4"
react-native-is-edge-to-edge "1.1.7"
react-native-rename@^2.9.0: react-native-rename@^2.9.0:
version "2.9.0" version "2.9.0"
...@@ -6852,6 +7078,16 @@ react-native-svg-charts@^5.4.0: ...@@ -6852,6 +7078,16 @@ react-native-svg-charts@^5.4.0:
d3-shape "^1.0.6" d3-shape "^1.0.6"
prop-types "^15.6.0" prop-types "^15.6.0"
react-native-svg-transformer@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/react-native-svg-transformer/-/react-native-svg-transformer-1.5.1.tgz#04467563becbea65b8e576df7e33c9a9a86961e8"
integrity sha512-dFvBNR8A9VPum9KCfh+LE49YiJEF8zUSnEFciKQroR/bEOhlPoZA0SuQ0qNk7m2iZl2w59FYjdRe0pMHWMDl0Q==
dependencies:
"@svgr/core" "^8.1.0"
"@svgr/plugin-jsx" "^8.1.0"
"@svgr/plugin-svgo" "^8.1.0"
path-dirname "^1.0.2"
react-native-svg@12.1.1: react-native-svg@12.1.1:
version "12.1.1" version "12.1.1"
resolved "https://registry.yarnpkg.com/react-native-svg/-/react-native-svg-12.1.1.tgz#5f292410b8bcc07bbc52b2da7ceb22caf5bcaaee" resolved "https://registry.yarnpkg.com/react-native-svg/-/react-native-svg-12.1.1.tgz#5f292410b8bcc07bbc52b2da7ceb22caf5bcaaee"
...@@ -7482,6 +7718,19 @@ slice-ansi@^2.0.0: ...@@ -7482,6 +7718,19 @@ slice-ansi@^2.0.0:
astral-regex "^1.0.0" astral-regex "^1.0.0"
is-fullwidth-code-point "^2.0.0" is-fullwidth-code-point "^2.0.0"
snake-case@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c"
integrity sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==
dependencies:
dot-case "^3.0.4"
tslib "^2.0.3"
source-map-js@^1.0.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
source-map-support@0.5.13: source-map-support@0.5.13:
version "0.5.13" version "0.5.13"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932"
...@@ -7732,6 +7981,24 @@ supports-preserve-symlinks-flag@^1.0.0: ...@@ -7732,6 +7981,24 @@ supports-preserve-symlinks-flag@^1.0.0:
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
svg-parser@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5"
integrity sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==
svgo@^3.0.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/svgo/-/svgo-3.3.2.tgz#ad58002652dffbb5986fc9716afe52d869ecbda8"
integrity sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==
dependencies:
"@trysound/sax" "0.2.0"
commander "^7.2.0"
css-select "^5.1.0"
css-tree "^2.3.1"
css-what "^6.1.0"
csso "^5.0.5"
picocolors "^1.0.0"
temp-dir@^2.0.0: temp-dir@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-2.0.0.tgz#bde92b05bdfeb1516e804c9c00ad45177f31321e" resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-2.0.0.tgz#bde92b05bdfeb1516e804c9c00ad45177f31321e"
...@@ -7823,7 +8090,7 @@ tslib@^1.8.1: ...@@ -7823,7 +8090,7 @@ tslib@^1.8.1:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
tslib@^2.0.1: tslib@^2.0.1, tslib@^2.0.3:
version "2.8.1" version "2.8.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment