diff --git a/packages/app/hooks/favorites/index.ts b/packages/app/hooks/favorites/index.ts deleted file mode 100644 index e883e1944..000000000 --- a/packages/app/hooks/favorites/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './useFetchUserFavorites'; -export * from './useAddFavorite'; diff --git a/packages/app/modules/feed/components/CreatedAtLabel/CreatedAtLabel.tsx b/packages/app/modules/feed/components/CreatedAtLabel/CreatedAtLabel.tsx new file mode 100644 index 000000000..3dba940d0 --- /dev/null +++ b/packages/app/modules/feed/components/CreatedAtLabel/CreatedAtLabel.tsx @@ -0,0 +1,18 @@ +import { XStack, RText } from '@packrat/ui'; +import { Clock } from '@tamagui/lucide-icons'; +import React, { type FC } from 'react'; + +interface CreatedAtLabelProps { + date: string; +} +export const CreatedAtLabel: FC = ({ date }) => { + return ( + + + {date} + + ); +}; diff --git a/packages/app/modules/feed/components/CreatedAtLabel/index.ts b/packages/app/modules/feed/components/CreatedAtLabel/index.ts new file mode 100644 index 000000000..81212b482 --- /dev/null +++ b/packages/app/modules/feed/components/CreatedAtLabel/index.ts @@ -0,0 +1 @@ +export { CreatedAtLabel } from './CreatedAtLabel'; diff --git a/packages/app/modules/feed/components/FavoriteButton/FavoriteButton.tsx b/packages/app/modules/feed/components/FavoriteButton/FavoriteButton.tsx new file mode 100644 index 000000000..ed56821eb --- /dev/null +++ b/packages/app/modules/feed/components/FavoriteButton/FavoriteButton.tsx @@ -0,0 +1,28 @@ +import { AntDesign } from '@expo/vector-icons'; +import { RText, XStack } from '@packrat/ui'; +import React, { type FC } from 'react'; +import { Pressable, type GestureResponderEvent } from 'react-native'; + +interface FavoriteButtonProps { + isAuthUserFavorite: boolean; + onClick: (e: GestureResponderEvent) => void; + count: number; +} +export const FavoriteButton: FC = ({ + onClick, + isAuthUserFavorite, + count, +}) => { + return ( + + + + + {count} + + ); +}; diff --git a/packages/app/modules/feed/components/FavoriteButton/index.ts b/packages/app/modules/feed/components/FavoriteButton/index.ts new file mode 100644 index 000000000..5647cda88 --- /dev/null +++ b/packages/app/modules/feed/components/FavoriteButton/index.ts @@ -0,0 +1 @@ +export { FavoriteButton } from './FavoriteButton'; diff --git a/packages/app/modules/feed/components/FeedCard.tsx b/packages/app/modules/feed/components/FeedCard/FeedCard.tsx similarity index 84% rename from packages/app/modules/feed/components/FeedCard.tsx rename to packages/app/modules/feed/components/FeedCard/FeedCard.tsx index 93c4426aa..5374ff139 100644 --- a/packages/app/modules/feed/components/FeedCard.tsx +++ b/packages/app/modules/feed/components/FeedCard/FeedCard.tsx @@ -1,49 +1,60 @@ -import { AntDesign } from '@expo/vector-icons'; -import { formatDistanceToNow } from 'date-fns'; -import { MaterialIcons, Entypo } from '@expo/vector-icons'; -import useTheme from 'app/hooks/useTheme'; -import { TouchableOpacity } from 'react-native'; -import { DuplicateIcon } from 'app/assets/icons'; -import { truncateString } from 'app/utils/truncateString'; -import { RLink, RText as OriginalRText, ContextMenu } from '@packrat/ui'; -import { formatNumber } from 'app/utils/formatNumber'; -import { useAddFavorite } from 'app/hooks/favorites'; +import React, { type FC } from 'react'; +import { type FeedItem, type FeedType } from 'app/modules/feed/model'; +import { feedItemPackCardConverter } from './utils'; +import { PackCard } from 'app/modules/pack'; +import { type CardType } from '@packrat/ui'; +import { useAddFavorite } from 'app/modules/feed'; import { useAuthUser } from 'app/modules/auth'; -import { useRouter } from 'app/hooks/router'; -import { useItemWeightUnit } from 'app/modules/item'; -import { convertWeight } from 'app/utils/convertWeight'; -import Layout from 'app/components/layout/Layout'; -import { Button, Card, H2, Paragraph, XStack, YStack } from 'tamagui'; - -const RText: any = OriginalRText; - -interface CardProps { - type: string; - id: string; - owner: { - id: string; - username: string; - }; - name: string; - total_weight: number; - is_public: boolean; - favorited_by: Array<{ - id: string; - }>; - favorites_count: number; - owner_id: string | { id: string }; - destination: string; - createdAt: string; - owners: Array<{ any: any }>; - duration: string; - itemPacks?: any[]; -} -interface User { - id: string; +const convertersByType = { + pack: feedItemPackCardConverter, +}; + +const cardComponentsByType = { + pack: PackCard, +}; + +interface FeedCardProps { + feedType: FeedType; + cardType: CardType; + item: FeedItem; } -export function FeedCard({ +export const FeedCard: FC = ({ item, cardType, feedType }) => { + const { addFavorite } = useAddFavorite(); + const user = useAuthUser(); + const cardProps = + typeof convertersByType[feedType] === 'function' + ? convertersByType[feedType](item, user?.id) + : null; + + const handleAddToFavorite = () => { + if (!user) return; + const data = { + packId: item.id, + userId: user.id, + }; + + addFavorite(data); + }; + + if (!cardProps) { + return null; + } + + const CardComponent = cardComponentsByType[feedType]; + + return ( + + ); +}; + +/* +export function FeedCardOld({ type, id, owner, @@ -58,7 +69,7 @@ export function FeedCard({ owners, duration, itemPacks, -}: CardProps) { +}: FeedCardProps) { console.log('CardProps:', favorited_by); const user = useAuthUser(); const { currentTheme } = useTheme(); @@ -246,3 +257,4 @@ export function FeedCard({ ); } +*/ diff --git a/packages/app/modules/feed/components/FeedCard/index.ts b/packages/app/modules/feed/components/FeedCard/index.ts new file mode 100644 index 000000000..a6871b085 --- /dev/null +++ b/packages/app/modules/feed/components/FeedCard/index.ts @@ -0,0 +1 @@ +export { FeedCard } from './FeedCard'; diff --git a/packages/app/modules/feed/components/FeedCard/utils.ts b/packages/app/modules/feed/components/FeedCard/utils.ts new file mode 100644 index 000000000..ea18d5b94 --- /dev/null +++ b/packages/app/modules/feed/components/FeedCard/utils.ts @@ -0,0 +1,40 @@ +import { type FeedCardProps, type FeedItem } from 'modules/feed/model'; +import { formatDistanceToNowStrict } from 'date-fns'; +import { type PackDetails } from 'app/modules/pack'; +import { truncateString } from 'app/utils/truncateString'; + +type Converter = ( + input: Input, + currentUserId?: string | number, +) => Result; + +export const feedItemPackCardConverter: Converter< + FeedItem, + Omit, 'cardType' | 'toggleFavorite'> +> = (input, currentUserId) => { + return { + id: input.id, + createdAt: formatDistanceToNowStrict(new Date(input.createdAt), { + addSuffix: false, + }), + title: truncateString(input.name, 25), + ownerId: + typeof input.owner_id === 'string' + ? input.owner_id + : input.owner_id?.id || '', + details: { + score: input.total_score, + weight: input.total_weight, + quantity: + input?.itemPacks?.reduce( + (accumulator, currentValue) => + accumulator + currentValue?.item?.quantity, + 0, + ) ?? 0, + }, + isUserFavorite: input?.userFavoritePacks?.some( + (obj) => obj?.userId === currentUserId, + ), + favoriteCount: input.favorites_count, + }; +}; diff --git a/packages/app/modules/feed/components/index.ts b/packages/app/modules/feed/components/index.ts index f2a5b6056..3c0d35173 100644 --- a/packages/app/modules/feed/components/index.ts +++ b/packages/app/modules/feed/components/index.ts @@ -1,3 +1,5 @@ export { FeedCard } from './FeedCard'; export { FeedSearchFilter } from './FeedSearchFilter'; export { SearchProvider, SearchContext } from './SearchProvider'; +export { FavoriteButton } from './FavoriteButton'; +export { CreatedAtLabel } from './CreatedAtLabel'; diff --git a/packages/app/modules/feed/hooks/index.ts b/packages/app/modules/feed/hooks/index.ts index a1ff51b2d..096f0b3da 100644 --- a/packages/app/modules/feed/hooks/index.ts +++ b/packages/app/modules/feed/hooks/index.ts @@ -1 +1,3 @@ export { useFeed } from './useFeed'; +export { useAddFavorite } from './useAddFavorite'; +export { useFetchUserFavorites } from './useFetchUserFavorites'; diff --git a/packages/app/hooks/favorites/useAddFavorite.ts b/packages/app/modules/feed/hooks/useAddFavorite.ts similarity index 95% rename from packages/app/hooks/favorites/useAddFavorite.ts rename to packages/app/modules/feed/hooks/useAddFavorite.ts index 41b5af9d8..b52c8796f 100644 --- a/packages/app/hooks/favorites/useAddFavorite.ts +++ b/packages/app/modules/feed/hooks/useAddFavorite.ts @@ -1,4 +1,4 @@ -import { queryTrpc } from '../../trpc'; +import { queryTrpc } from 'app/trpc'; export function useAddFavorite() { const utils = queryTrpc.useContext(); diff --git a/packages/app/hooks/favorites/useFetchUserFavorites.ts b/packages/app/modules/feed/hooks/useFetchUserFavorites.ts similarity index 90% rename from packages/app/hooks/favorites/useFetchUserFavorites.ts rename to packages/app/modules/feed/hooks/useFetchUserFavorites.ts index cfb3e6c39..978fc6bff 100644 --- a/packages/app/hooks/favorites/useFetchUserFavorites.ts +++ b/packages/app/modules/feed/hooks/useFetchUserFavorites.ts @@ -1,4 +1,4 @@ -import { queryTrpc } from '../../trpc'; +import { queryTrpc } from 'app/trpc'; export const useFetchUserFavorites = (userId: string) => { const enabled = !!userId; diff --git a/packages/app/modules/feed/index.ts b/packages/app/modules/feed/index.ts index 94d9e2c14..1b1318e65 100644 --- a/packages/app/modules/feed/index.ts +++ b/packages/app/modules/feed/index.ts @@ -1,4 +1,11 @@ export * from './widgets'; export * from './screens'; -export { useFeed } from './hooks'; -export { SearchProvider, FeedSearchFilter, FeedCard } from './components'; +export { useFeed, useAddFavorite, useFetchUserFavorites } from './hooks'; +export { + SearchProvider, + FeedSearchFilter, + FeedCard, + FavoriteButton, + CreatedAtLabel, +} from './components'; +export { type FeedCardProps, type FeedItem } from './model'; diff --git a/packages/app/modules/feed/model.ts b/packages/app/modules/feed/model.ts new file mode 100644 index 000000000..cc30e1659 --- /dev/null +++ b/packages/app/modules/feed/model.ts @@ -0,0 +1,39 @@ +import { type CardType } from '@packrat/ui'; + +export type FeedType = 'pack'; + +export interface FeedItem { + id: string; + owner: { + id: string; + username: string; + }; + name: string; + total_weight: number; + total_score: number; + is_public: boolean; + favorited_by: Array<{ + id: string; + }>; + userFavoritePacks?: Array<{ userId: string }>; + favorites_count: number; + owner_id: string | { id: string }; + destination: string; + createdAt: string; + owners: Array<{ any: any }>; + duration: string; + type: FeedType; + itemPacks?: any[]; +} + +export interface FeedCardProps
{ + id: string; + title: string; + cardType: CardType; + createdAt: string; + details: Details; + ownerId: string; + favoriteCount: number; + isUserFavorite: boolean; + toggleFavorite: () => void; +} diff --git a/packages/app/modules/feed/screens/FeedScreen.tsx b/packages/app/modules/feed/screens/FeedScreen.tsx index db33bb99b..70c766d2e 100644 --- a/packages/app/modules/feed/screens/FeedScreen.tsx +++ b/packages/app/modules/feed/screens/FeedScreen.tsx @@ -149,13 +149,16 @@ const Feed = ({ feedType = 'public' }: FeedProps) => { ( + + )} keyExtractor={(item) => item?.id + item?.type} renderItem={({ item }) => ( )} ListFooterComponent={() => } diff --git a/packages/app/modules/feed/widgets/FeedPreview/FeedPreview.tsx b/packages/app/modules/feed/widgets/FeedPreview/FeedPreview.tsx index 33bf44dd2..02723e320 100644 --- a/packages/app/modules/feed/widgets/FeedPreview/FeedPreview.tsx +++ b/packages/app/modules/feed/widgets/FeedPreview/FeedPreview.tsx @@ -1,8 +1,9 @@ import React from 'react'; import Carousel from 'app/components/carousel'; import { useFeed } from '../../hooks'; -import { default as FeedPreviewCard, type FeedItem } from './FeedPreviewCard'; import Loader from 'app/components/Loader'; +import { FeedCard, type FeedItem } from 'app/modules/feed'; +import { View } from 'tamagui'; interface FeedPreviewScrollProps { itemWidth: number; @@ -24,9 +25,11 @@ const FeedPreviewScroll: React.FC = ({ {feedData ?.filter((item): item is FeedItem => item.type !== null) .map((item: FeedItem) => { - const linkStr = `/${item.type}/${item.id}`; + const linkStr = `/pack/${item.id}`; return linkStr ? ( - + + + ) : null; })} diff --git a/packages/app/modules/feed/widgets/FeedPreview/FeedPreviewCard.tsx b/packages/app/modules/feed/widgets/FeedPreview/FeedPreviewCard.tsx deleted file mode 100644 index 0453036d8..000000000 --- a/packages/app/modules/feed/widgets/FeedPreview/FeedPreviewCard.tsx +++ /dev/null @@ -1,240 +0,0 @@ -/* eslint-disable react/prop-types */ -/* eslint-disable react/react-in-jsx-scope */ -import { useState } from 'react'; -import { RText as OriginalRText, RStack } from '@packrat/ui'; -import { RLink } from '@packrat/ui'; -import { type LayoutChangeEvent, View } from 'react-native'; -import useCustomStyles from 'app/hooks/useCustomStyles'; -import loadStyles from './feedpreview.style'; -import { AntDesign, Fontisto, MaterialIcons } from '@expo/vector-icons'; -import useTheme from 'app/hooks/useTheme'; -import { useItemWeightUnit } from 'app/modules/item'; -import { convertWeight } from 'app/utils/convertWeight'; -import { formatNumber } from 'app/utils/formatNumber'; -import { hexToRGBA } from 'app/utils/colorFunctions'; - -// TODO FeedItem is one of: trip, pack, similar pack & item -export type FeedItem = any; - -interface FeedPreviewCardProps { - linkStr: string; - item: FeedItem; - feedType: string; -} - -const RText: any = OriginalRText; - -const FeedPreviewCard: React.FC = ({ - linkStr, - item, - feedType, -}) => { - const { currentTheme } = useTheme(); - const styles = useCustomStyles(loadStyles); - const [weightUnit] = useItemWeightUnit(); - const formattedWeight = convertWeight( - item.total_weight ?? item.weight, - item.unit ?? 'g', - weightUnit, - ); - const [cardWidth, setCardWidth] = useState(); - - const handleSetCardWidth = (event: LayoutChangeEvent) => { - const { width } = event.nativeEvent.layout; - setCardWidth(width); - }; - - if (feedType == 'similarItems') { - return ( - - - - - - - - - {item.name} - - - - {formatNumber(formattedWeight)} - {weightUnit} - - - - Qty: {item.quantity} - - - - - {new Date(item.createdAt).toLocaleString('en-US', { - month: 'short', - day: '2-digit', - ...(new Date(item.createdAt).getFullYear() == - new Date().getFullYear() - ? {} - : { year: 'numeric' }), - })} - - - - - - ); - } - - return ( - - - - - - - - - - {item.name} - - - - {formatNumber(formattedWeight)} - {weightUnit} - - - - - {item.favorites_count} - - - - - - {new Date(item.createdAt).toLocaleString('en-US', { - month: 'short', - day: '2-digit', - ...(new Date(item.createdAt).getFullYear() == - new Date().getFullYear() - ? {} - : { year: 'numeric' }), - })} - - - - Ttl Score: {item.total_score} - - - - - - ); -}; - -export default FeedPreviewCard; diff --git a/packages/app/modules/pack/components/PackCard/PackCard.tsx b/packages/app/modules/pack/components/PackCard/PackCard.tsx new file mode 100644 index 000000000..8c7f42f69 --- /dev/null +++ b/packages/app/modules/pack/components/PackCard/PackCard.tsx @@ -0,0 +1,18 @@ +import React, { type FC } from 'react'; +import { type FeedCardProps } from 'app/modules/feed'; +import { type PackDetails } from 'app/modules/pack/model'; +import { PackPrimaryCard } from './PackPrimaryCard'; +import { PackSecondaryCard } from './PackSecondaryCard'; + +interface PackCardProps extends FeedCardProps {} + +const PackCards = { + primary: PackPrimaryCard, + secondary: PackSecondaryCard, +}; + +export const PackCard: FC = (props) => { + const PackCardComponent = PackCards[props.cardType]; + + return ; +}; diff --git a/packages/app/modules/pack/components/PackCard/PackImage.tsx b/packages/app/modules/pack/components/PackCard/PackImage.tsx new file mode 100644 index 000000000..868841090 --- /dev/null +++ b/packages/app/modules/pack/components/PackCard/PackImage.tsx @@ -0,0 +1,29 @@ +import React, { type FC } from 'react'; +import { View } from '@packrat/ui'; +import { Package } from '@tamagui/lucide-icons'; +import { type ViewProps } from 'tamagui'; + +interface PackImageProps { + style?: ViewProps['style']; +} + +export const PackImage: FC = ({ style = {} }) => { + return ( + + + + + + ); +}; diff --git a/packages/app/modules/pack/components/PackCard/PackPrimaryCard.tsx b/packages/app/modules/pack/components/PackCard/PackPrimaryCard.tsx new file mode 100644 index 000000000..4c624cded --- /dev/null +++ b/packages/app/modules/pack/components/PackCard/PackPrimaryCard.tsx @@ -0,0 +1,59 @@ +import { Card, Details, RLink, RStack, RText } from '@packrat/ui'; +import React, { type FC } from 'react'; +import { PackImage } from './PackImage'; +import { + FavoriteButton, + CreatedAtLabel, + type FeedCardProps, +} from 'app/modules/feed'; +import { type PackDetails } from 'app/modules/pack/model'; +import { DuplicateIcon } from 'app/assets/icons'; +import { useItemWeightUnit } from 'app/modules/item'; +import { convertWeight } from 'app/utils/convertWeight'; + +interface PackCardProps extends FeedCardProps {} + +export const PackPrimaryCard: FC = (props) => { + const [weightUnit] = useItemWeightUnit(); + const packDetails = Object.entries(props.details).map(([key, value]) => ({ + key, + label: key, + value: + key === 'weight' + ? `${convertWeight(value, 'g', weightUnit)} ${weightUnit}` + : value, + })); + + return ( + } + subtitle={} + actions={ + + { + e.stopPropagation(); + e.preventDefault(); + props.toggleFavorite(); + }} + /> + + + + + View owner + + + } + content={
} + type={props.cardType} + /> + ); +}; diff --git a/packages/app/modules/pack/components/PackCard/PackSecondaryCard.tsx b/packages/app/modules/pack/components/PackCard/PackSecondaryCard.tsx new file mode 100644 index 000000000..1f222a45f --- /dev/null +++ b/packages/app/modules/pack/components/PackCard/PackSecondaryCard.tsx @@ -0,0 +1,38 @@ +import { Card, RStack } from '@packrat/ui'; +import React, { type FC } from 'react'; +import { PackImage } from './PackImage'; +import { + CreatedAtLabel, + FavoriteButton, + type FeedCardProps, +} from 'app/modules/feed'; +import { type PackDetails } from 'app/modules/pack/model'; + +interface PackCardProps extends FeedCardProps {} + +export const PackSecondaryCard: FC = (props) => { + return ( + + } + subtitle={} + actions={ + + { + e.stopPropagation(); + e.preventDefault(); + props.toggleFavorite(); + }} + /> + + } + type={props.cardType} + /> + ); +}; diff --git a/packages/app/modules/pack/components/PackCard/index.ts b/packages/app/modules/pack/components/PackCard/index.ts new file mode 100644 index 000000000..184fc6a3b --- /dev/null +++ b/packages/app/modules/pack/components/PackCard/index.ts @@ -0,0 +1 @@ +export { PackCard } from './PackCard'; diff --git a/packages/app/modules/pack/components/index.ts b/packages/app/modules/pack/components/index.ts index 3c3c80097..3fa8751e1 100644 --- a/packages/app/modules/pack/components/index.ts +++ b/packages/app/modules/pack/components/index.ts @@ -5,3 +5,4 @@ export { EditPackItemModal, } from './PackTable'; export { CopyPackModal } from './CopyPackModal'; +export { PackCard } from './PackCard'; diff --git a/packages/app/modules/pack/index.ts b/packages/app/modules/pack/index.ts index de11d3544..d60ba23cc 100644 --- a/packages/app/modules/pack/index.ts +++ b/packages/app/modules/pack/index.ts @@ -1,3 +1,4 @@ export * from './hooks'; export * from './components'; export { AddPackScreen, PackDetailsScreen } from './screens'; +export { type PackDetails } from './model'; diff --git a/packages/app/modules/pack/model.ts b/packages/app/modules/pack/model.ts new file mode 100644 index 000000000..960e743ab --- /dev/null +++ b/packages/app/modules/pack/model.ts @@ -0,0 +1,5 @@ +export interface PackDetails { + score?: number; + quantity: number; + weight: number; +} diff --git a/packages/app/modules/user/components/UserDataCard.tsx b/packages/app/modules/user/components/UserDataCard.tsx index fdbe319ac..e28684ba4 100644 --- a/packages/app/modules/user/components/UserDataCard.tsx +++ b/packages/app/modules/user/components/UserDataCard.tsx @@ -14,7 +14,7 @@ import { truncateString } from 'app/utils/truncateString'; import { useEditPack } from 'app/modules/pack'; import { Platform } from 'react-native'; import { useEditTrips } from 'app/hooks/trips'; -import { useAddFavorite } from 'app/hooks/favorites'; +import { useAddFavorite } from 'app/modules/feed'; import useTheme from 'app/hooks/useTheme'; const RText: any = OriginalRText; diff --git a/packages/app/modules/user/components/UserDetailList.tsx b/packages/app/modules/user/components/UserDetailList.tsx index 5fa4aa831..2b2a92386 100644 --- a/packages/app/modules/user/components/UserDetailList.tsx +++ b/packages/app/modules/user/components/UserDetailList.tsx @@ -51,7 +51,9 @@ export const UserDataList = ({ data }: DataListProps) => { ]} footerComponent={undefined} > - + { data={filteredData.slice(0, 2)} horizontal={false} keyExtractor={(item) => item?.id} + ItemSeparatorComponent={() => } renderItem={({ item }) => ( - + )} showsVerticalScrollIndicator={false} maxToRenderPerBatch={2} @@ -93,8 +101,14 @@ export const UserDataList = ({ data }: DataListProps) => { data={filteredData} horizontal={false} keyExtractor={(item) => item?._id} + ItemSeparatorComponent={() => } renderItem={({ item }) => ( - + )} showsVerticalScrollIndicator={false} maxToRenderPerBatch={2} diff --git a/packages/app/modules/user/hooks/useProfile.ts b/packages/app/modules/user/hooks/useProfile.ts index 938b2a0e4..88057499e 100644 --- a/packages/app/modules/user/hooks/useProfile.ts +++ b/packages/app/modules/user/hooks/useProfile.ts @@ -1,4 +1,4 @@ -import { useFetchUserFavorites } from 'app/hooks/favorites'; +import { useFetchUserFavorites } from 'app/modules/feed'; import { useUserPacks } from 'app/modules/pack'; import { useUserTrips } from 'app/hooks/singletrips'; import { useAuthUser, useMatchesCurrentUser } from 'app/modules/auth'; diff --git a/packages/ui/src/Details/Details.tsx b/packages/ui/src/Details/Details.tsx new file mode 100644 index 000000000..34b772ce8 --- /dev/null +++ b/packages/ui/src/Details/Details.tsx @@ -0,0 +1,49 @@ +import React, { type FC, type ReactNode } from 'react'; +import { ListItem, type ListItemProps, YGroup, Text, useTheme } from 'tamagui'; + +interface DetailsItem extends ListItemProps { + key: string | number; + label: ReactNode; + value: ReactNode; +} +interface DetailsProps { + items: DetailsItem[]; +} +export const Details: FC = ({ items }) => { + const theme = useTheme(); + const primaryColor = theme.primary; + + return ( + + {items.map(({ key, label, value, ...item }) => ( + + + + {label}: + + {value} + + + ))} + + ); +}; diff --git a/packages/ui/src/Details/index.ts b/packages/ui/src/Details/index.ts new file mode 100644 index 000000000..b358dd569 --- /dev/null +++ b/packages/ui/src/Details/index.ts @@ -0,0 +1 @@ +export { Details } from './Details'; diff --git a/packages/ui/src/card/Card.tsx b/packages/ui/src/card/Card.tsx new file mode 100644 index 000000000..d06775412 --- /dev/null +++ b/packages/ui/src/card/Card.tsx @@ -0,0 +1,23 @@ +import React, { type FC } from 'react'; +import { type CardType, type BaseCardProps } from './model'; +import { PrimaryCard } from './PrimaryCard'; +import { SecondaryCard } from './SecondaryCard'; +import { RLink } from '..'; + +const CardComponents = { + primary: PrimaryCard, + secondary: SecondaryCard, +}; + +interface CardProps extends BaseCardProps { + type: CardType; +} +export const Card: FC = ({ type, ...props }) => { + const CardComponent = CardComponents[type]; + + return ( + + + + ); +}; diff --git a/packages/ui/src/card/PrimaryCard.tsx b/packages/ui/src/card/PrimaryCard.tsx new file mode 100644 index 000000000..d10a799fb --- /dev/null +++ b/packages/ui/src/card/PrimaryCard.tsx @@ -0,0 +1,53 @@ +import React, { type FC } from 'react'; +import { type BaseCardProps } from './model'; +import { Card, useTheme, View, XStack, YStack } from 'tamagui'; +import { RText } from '@packrat/ui'; + +interface PrimaryCardProps extends BaseCardProps {} + +export const PrimaryCard: FC = (props) => { + const theme = useTheme(); + return ( + + + + + {props.image} + + + + {props.title} + + + {props.subtitle} + + {props.actions} + + + + + {props.content} + + + {props.footer} + + + ); +}; diff --git a/packages/ui/src/card/SecondaryCard.tsx b/packages/ui/src/card/SecondaryCard.tsx new file mode 100644 index 000000000..2459c44ac --- /dev/null +++ b/packages/ui/src/card/SecondaryCard.tsx @@ -0,0 +1,54 @@ +import React, { type FC } from 'react'; +import { type BaseCardProps } from './model'; +import { Card, View, XStack, YStack } from 'tamagui'; +import RText from '../RText'; + +interface SecondaryCardProps extends Omit {} + +export const SecondaryCard: FC = (props) => { + return ( + + {props.image} + + + + + {props.title} + + + {props.subtitle} + + + {props.actions} + + + + ); +}; diff --git a/packages/ui/src/card/index.ts b/packages/ui/src/card/index.ts new file mode 100644 index 000000000..9211d038e --- /dev/null +++ b/packages/ui/src/card/index.ts @@ -0,0 +1,2 @@ +export { Card } from './Card'; +export * from './model'; diff --git a/packages/ui/src/card/model.ts b/packages/ui/src/card/model.ts new file mode 100644 index 000000000..ce048554f --- /dev/null +++ b/packages/ui/src/card/model.ts @@ -0,0 +1,13 @@ +import { type ReactNode } from 'react'; + +export type CardType = 'primary' | 'secondary'; + +export interface BaseCardProps { + link: string; + title: ReactNode; + image: ReactNode; + subtitle: ReactNode; + content?: ReactNode; + footer?: ReactNode; + actions?: ReactNode; +} diff --git a/packages/ui/src/index.tsx b/packages/ui/src/index.tsx index d9932dbd9..a6dc17b74 100644 --- a/packages/ui/src/index.tsx +++ b/packages/ui/src/index.tsx @@ -78,7 +78,9 @@ export { export * from './InputText'; export * from './form'; - +export * from './card'; +export * from './Details'; +export { View } from 'tamagui'; export { config } from './tamagui.config'; // export * from 'tamagui'; export * from '@tamagui/toast';