diff --git a/src/entity/cache/CacheItem.js b/src/entity/cache/CacheItem.js
new file mode 100644
index 0000000..7c3bf10
--- /dev/null
+++ b/src/entity/cache/CacheItem.js
@@ -0,0 +1,89 @@
+import { Pressable, StyleSheet, Text, View } from "react-native";
+
+const CacheItem = ({ cache, isFound, onSelect }) => {
+ // Initialisations ---------------------
+ // State -------------------------------
+ // Handlers ----------------------------
+
+ const handleSelect = () => {
+ onSelect(cache);
+ };
+
+ // View --------------------------------
+
+ return (
+ [styles.item, pressed && styles.pressedItem]}
+ >
+
+
+ {cache.CacheName}
+
+
+ {isFound ? "Found" : "Open"}
+
+
+ {cache.CacheClue ? (
+
+ {cache.CacheClue}
+
+ ) : null}
+ Points: {cache.CachePoints ?? 0}
+
+ );
+};
+
+const styles = StyleSheet.create({
+ item: {
+ paddingVertical: 14,
+ paddingHorizontal: 2,
+ borderTopWidth: 1,
+ borderColor: "lightgray",
+ gap: 4,
+ },
+ pressedItem: {
+ backgroundColor: "#f0f9ff",
+ },
+ row: {
+ flexDirection: "row",
+ alignItems: "center",
+ justifyContent: "space-between",
+ },
+ name: {
+ fontSize: 16,
+ fontWeight: "600",
+ color: "#111827",
+ flex: 1,
+ marginRight: 8,
+ },
+ clue: {
+ fontSize: 13,
+ color: "#6b7280",
+ },
+ points: {
+ fontSize: 12,
+ fontWeight: "500",
+ color: "#2563eb",
+ },
+ badge: {
+ paddingHorizontal: 8,
+ paddingVertical: 2,
+ borderRadius: 10,
+ },
+ badgeFound: {
+ backgroundColor: "#d1fae5",
+ },
+ badgeOpen: {
+ backgroundColor: "#fef3c7",
+ },
+ badgeText: {
+ fontSize: 11,
+ fontWeight: "600",
+ color: "#374151",
+ },
+});
+
+export default CacheItem;
diff --git a/src/entity/cache/CacheList.js b/src/entity/cache/CacheList.js
new file mode 100644
index 0000000..caee2fe
--- /dev/null
+++ b/src/entity/cache/CacheList.js
@@ -0,0 +1,28 @@
+import { ScrollView, StyleSheet } from "react-native";
+import CacheItem from "./CacheItem";
+
+const CacheList = ({ caches, foundCacheIds, onSelect }) => {
+ // Initialisations ---------------------
+ // State -------------------------------
+ // Handlers ----------------------------
+ // View --------------------------------
+
+ return (
+
+ {caches.map((cache) => (
+
+ ))}
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {},
+});
+
+export default CacheList;
diff --git a/src/entity/event/EventItem.js b/src/entity/event/EventItem.js
new file mode 100644
index 0000000..a0e196b
--- /dev/null
+++ b/src/entity/event/EventItem.js
@@ -0,0 +1,100 @@
+import { Pressable, StyleSheet, Text, View } from "react-native";
+
+const EventItem = ({ event, onSelect }) => {
+ // Initialisations ---------------------
+ // State -------------------------------
+ // Handlers ----------------------------
+
+ const handleSelect = () => {
+ onSelect(event);
+ };
+
+ const formatDate = (dt) => {
+ if (!dt) return "โ";
+ return new Date(dt).toLocaleDateString();
+ };
+
+ // View --------------------------------
+
+ return (
+ [styles.item, pressed && styles.pressedItem]}
+ >
+
+
+ {event.EventName}
+
+
+
+ {event.EventIspublic ? "Public" : "Private"}
+
+
+
+ {event.EventDescription ? (
+
+ {event.EventDescription}
+
+ ) : null}
+
+ {formatDate(event.EventStart)} โ {formatDate(event.EventFinish)}
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ item: {
+ paddingVertical: 14,
+ paddingHorizontal: 2,
+ borderTopWidth: 1,
+ borderColor: "lightgray",
+ gap: 4,
+ },
+ pressedItem: {
+ backgroundColor: "#f0f9ff",
+ },
+ row: {
+ flexDirection: "row",
+ alignItems: "center",
+ justifyContent: "space-between",
+ },
+ name: {
+ fontSize: 16,
+ fontWeight: "600",
+ color: "#111827",
+ flex: 1,
+ marginRight: 8,
+ },
+ description: {
+ fontSize: 13,
+ color: "#6b7280",
+ },
+ meta: {
+ fontSize: 12,
+ color: "#9ca3af",
+ },
+ badge: {
+ paddingHorizontal: 8,
+ paddingVertical: 2,
+ borderRadius: 10,
+ },
+ badgePublic: {
+ backgroundColor: "#dcfce7",
+ },
+ badgePrivate: {
+ backgroundColor: "#f3f4f6",
+ },
+ badgeText: {
+ fontSize: 11,
+ fontWeight: "600",
+ color: "#374151",
+ },
+});
+
+export default EventItem;
diff --git a/src/entity/event/EventList.js b/src/entity/event/EventList.js
new file mode 100644
index 0000000..bbab75d
--- /dev/null
+++ b/src/entity/event/EventList.js
@@ -0,0 +1,23 @@
+import { ScrollView, StyleSheet } from "react-native";
+import EventItem from "./EventItem";
+
+const EventList = ({ events, onSelect }) => {
+ // Initialisations ---------------------
+ // State -------------------------------
+ // Handlers ----------------------------
+ // View --------------------------------
+
+ return (
+
+ {events.map((event) => (
+
+ ))}
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {},
+});
+
+export default EventList;
diff --git a/src/entity/find/FindItem.js b/src/entity/find/FindItem.js
new file mode 100644
index 0000000..5e1dccf
--- /dev/null
+++ b/src/entity/find/FindItem.js
@@ -0,0 +1,65 @@
+import { StyleSheet, Text, View } from "react-native";
+
+const FindItem = ({ find }) => {
+ // Initialisations ---------------------
+ // State -------------------------------
+ // Handlers ----------------------------
+
+ const formatDate = (dt) => {
+ if (!dt) return "โ";
+ return new Date(dt).toLocaleString();
+ };
+
+ const cacheName = find.FindCache?.CacheName ?? `Cache #${find.FindCacheID}`;
+ const points = find.FindCache?.CachePoints ?? 0;
+ const username =
+ find.FindPlayer?.PlayerUser?.UserUsername ?? `Player #${find.FindPlayerID}`;
+
+ // View --------------------------------
+
+ return (
+
+
+
+ {cacheName}
+
+ +{points} pts
+
+
+ {username} ยท {formatDate(find.FindDatetime)}
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ item: {
+ paddingVertical: 12,
+ borderTopWidth: 1,
+ borderColor: "lightgray",
+ gap: 3,
+ },
+ row: {
+ flexDirection: "row",
+ justifyContent: "space-between",
+ alignItems: "center",
+ },
+ cacheName: {
+ fontSize: 15,
+ fontWeight: "600",
+ color: "#111827",
+ flex: 1,
+ marginRight: 8,
+ },
+ points: {
+ fontSize: 14,
+ fontWeight: "700",
+ color: "#16a34a",
+ },
+ meta: {
+ fontSize: 12,
+ color: "#9ca3af",
+ },
+});
+
+export default FindItem;
diff --git a/src/entity/find/FindList.js b/src/entity/find/FindList.js
new file mode 100644
index 0000000..463c236
--- /dev/null
+++ b/src/entity/find/FindList.js
@@ -0,0 +1,23 @@
+import { ScrollView, StyleSheet } from "react-native";
+import FindItem from "./FindItem";
+
+const FindList = ({ finds }) => {
+ // Initialisations ---------------------
+ // State -------------------------------
+ // Handlers ----------------------------
+ // View --------------------------------
+
+ return (
+
+ {finds.map((find) => (
+
+ ))}
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {},
+});
+
+export default FindList;