feat: redesign section progress, report, link, file, dan cancel pada project & division/task
- SectionProgress: progress bar animated, badge persentase, label status, task count - SectionReport: header ikon, left accent border, TextExpandable dengan label Indonesia - SectionLink: tap langsung buka URL, ikon per domain, long press untuk hapus - SectionFile: icon container konsisten 30×30 di semua section - SectionCancel: card subtle dengan warna error, konsisten dengan visual language baru - TextExpandable: fix bug show/hide tidak muncul setelah content diupdate - Tambah 14 style class baru di Styles.ts untuk menggantikan inline style - Terapkan semua perubahan ke fitur division/task - Fix menu "Edit Tugas" di sectionTanggalTugasTask yang terpotong karena overflow Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
100
components/project/itemSectionLink.tsx
Normal file
100
components/project/itemSectionLink.tsx
Normal file
@@ -0,0 +1,100 @@
|
||||
import { urlCompleted } from "@/lib/fun_urlCompleted";
|
||||
import { useTheme } from "@/providers/ThemeProvider";
|
||||
import { MaterialCommunityIcons } from "@expo/vector-icons";
|
||||
import { Linking, Pressable, View } from "react-native";
|
||||
import Text from "../Text";
|
||||
import Styles from "@/constants/Styles";
|
||||
|
||||
type Props = {
|
||||
link: string
|
||||
canDelete: boolean
|
||||
onLongPress: () => void
|
||||
}
|
||||
|
||||
type DomainConfig = {
|
||||
icon: keyof typeof MaterialCommunityIcons.glyphMap
|
||||
color: string
|
||||
label: string
|
||||
}
|
||||
|
||||
function getDomainConfig(url: string): DomainConfig {
|
||||
try {
|
||||
const hostname = new URL(urlCompleted(url)).hostname.replace('www.', '')
|
||||
if (hostname.includes('youtube.com') || hostname.includes('youtu.be'))
|
||||
return { icon: 'youtube', color: '#FF0000', label: 'YouTube' }
|
||||
if (hostname.includes('drive.google.com'))
|
||||
return { icon: 'google-drive', color: '#4285F4', label: 'Google Drive' }
|
||||
if (hostname.includes('docs.google.com'))
|
||||
return { icon: 'google', color: '#4285F4', label: 'Google Docs' }
|
||||
if (hostname.includes('sheets.google.com'))
|
||||
return { icon: 'google-spreadsheet', color: '#0F9D58', label: 'Google Sheets' }
|
||||
if (hostname.includes('github.com'))
|
||||
return { icon: 'github', color: '#24292E', label: 'GitHub' }
|
||||
if (hostname.includes('wa.me') || hostname.includes('whatsapp.com'))
|
||||
return { icon: 'whatsapp', color: '#25D366', label: 'WhatsApp' }
|
||||
if (hostname.includes('instagram.com'))
|
||||
return { icon: 'instagram', color: '#E1306C', label: 'Instagram' }
|
||||
if (hostname.includes('facebook.com'))
|
||||
return { icon: 'facebook', color: '#1877F2', label: 'Facebook' }
|
||||
if (hostname.includes('figma.com'))
|
||||
return { icon: 'vector-bezier', color: '#F24E1E', label: 'Figma' }
|
||||
if (hostname.includes('notion.so'))
|
||||
return { icon: 'notebook-outline', color: '#000000', label: 'Notion' }
|
||||
return { icon: 'link-variant', color: '#6366F1', label: hostname }
|
||||
} catch {
|
||||
return { icon: 'link-variant', color: '#6366F1', label: url }
|
||||
}
|
||||
}
|
||||
|
||||
function getDisplayUrl(url: string) {
|
||||
try {
|
||||
const full = urlCompleted(url)
|
||||
const parsed = new URL(full)
|
||||
const path = parsed.pathname + parsed.search
|
||||
return path.length > 1 ? path : ''
|
||||
} catch {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
export default function ItemSectionLink({ link, canDelete, onLongPress }: Props) {
|
||||
const { colors, activeTheme } = useTheme()
|
||||
const config = getDomainConfig(link)
|
||||
const displayPath = getDisplayUrl(link)
|
||||
|
||||
const iconBg = activeTheme === 'dark' ? config.color + '25' : config.color + '15'
|
||||
const iconColor = activeTheme === 'dark' && config.color === '#24292E' ? '#ECEDEE' : config.color
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
onPress={() => Linking.openURL(urlCompleted(link))}
|
||||
onLongPress={canDelete ? onLongPress : undefined}
|
||||
style={({ pressed }) => ([
|
||||
Styles.fileCard,
|
||||
{
|
||||
width: '100%',
|
||||
marginBottom: 10,
|
||||
borderColor: colors.icon + '18',
|
||||
backgroundColor: pressed ? colors.icon + '10' : colors.card,
|
||||
},
|
||||
])}
|
||||
>
|
||||
<View style={[Styles.sectionIconBox, { backgroundColor: iconBg }]}>
|
||||
<MaterialCommunityIcons name={config.icon} size={18} color={iconColor} />
|
||||
</View>
|
||||
|
||||
<View style={Styles.flex1}>
|
||||
<Text style={[Styles.textDefaultSemiBold, { color: colors.text }]} numberOfLines={1}>
|
||||
{config.label}
|
||||
</Text>
|
||||
{displayPath.length > 0 && (
|
||||
<Text style={[Styles.textSmallSemiBold, { color: colors.dimmed, marginTop: 2 }]} numberOfLines={1} ellipsizeMode="tail">
|
||||
{displayPath}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
|
||||
<MaterialCommunityIcons name="arrow-top-right" size={16} color={colors.dimmed} />
|
||||
</Pressable>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user