Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,39 @@ Edit `frontend/App.js`:
- Set up CI/CD pipeline
- Deploy to production environment

## Development Workflow
### Branch naming conventions + Conventional Commits
We follow the Conventional Commits specification for commit messages. This ensures a consistent commit history and enables automated versioning and changelog generation.

### Branch Naming Conventions
```
<type>/<short-description>
```
Examples:
- feature/{feature-name}
- fix/{bug-description}
- docs/{documentation-change}
### Commit Message
Follow this structure for commit messages
```
<type>(<scope>): <subject>
```
Where:
```type``` has one of the following:
- feat: New features
- fix: Bug fixes
- docs: Documentation changes
- style: Code formatting only
- refactor: Code changes without behavior change
- test: Adding or updating tests
- chore: Build process or tooling updates

### PR process
- Fork or branch from main
- Create a PR with a clear description
- Ensure checks pass (build, lint, test)
- Request review before merging

## License

MIT
5 changes: 4 additions & 1 deletion frontend/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Welcome to your Expo app 👋
# Longhorn Studies

This is an [Expo](https://expo.dev) project created with [`create-expo-app`](https://www.npmjs.com/package/create-expo-app).

Expand Down Expand Up @@ -48,3 +48,6 @@ Join our community of developers creating universal apps.

- [Expo on GitHub](https://github.com/expo/expo): View our open source platform and contribute.
- [Discord community](https://chat.expo.dev): Chat with Expo users and ask questions.


u gotta use WSL
3 changes: 2 additions & 1 deletion frontend/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
},
"web": {
"output": "static",
"favicon": "./assets/images/favicon.png"
"favicon": "./assets/images/favicon.png",
"bundler": "metro"
},
"plugins": [
"expo-router",
Expand Down
4 changes: 3 additions & 1 deletion frontend/app/(tabs)/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ export default function TabLayout() {
tabBarActiveTintColor: Colors[colorScheme ?? 'light'].tint,
headerShown: false,
tabBarButton: HapticTab,
}}>
}}

>
<Tabs.Screen
name="index"
options={{
Expand Down
8 changes: 6 additions & 2 deletions frontend/app/(tabs)/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { Image } from 'expo-image';
import { Platform, StyleSheet } from 'react-native';
import { Platform, StyleSheet, View, Text } from 'react-native';

import { HelloWave } from '@/components/hello-wave';
import ParallaxScrollView from '@/components/parallax-scroll-view';
import { ThemedText } from '@/components/themed-text';
import { ThemedView } from '@/components/themed-view';
import { Link } from 'expo-router';

import OpenStatus from '@/components/ui/open-status';
import StudySpotCard from '@/components/ui/study-spot-card';

export default function HomeScreen() {
return (
<ParallaxScrollView
Expand All @@ -16,7 +19,8 @@ export default function HomeScreen() {
source={require('@/assets/images/partial-react-logo.png')}
style={styles.reactLogo}
/>
}>
}
>
<ThemedView style={styles.titleContainer}>
<ThemedText type="title">Welcome!</ThemedText>
<HelloWave />
Expand Down
21 changes: 21 additions & 0 deletions frontend/app/(tabs)/test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Image } from "expo-image";
import { Platform, StyleSheet, View, Text } from "react-native";

import StudySpotCard from "@/components/ui/study-spot-card";

export default function HomeScreen() {
return (
<View className="p-4">
<Image
source="https://picsum.photos/seed/696/3000/2000"
contentFit="cover"
transition={1000}
className="w-full h-64 rounded-xl"
style={{ width: '100%', height: 250 }}
/>
<View className="h-64" />
<View className="h-16" />
<StudySpotCard buildingName="Union" locationName="Ballroom" location="Ballroom" hours={["5:00am", "5:00pm"]} tags={["Hi"]}/>
</View>
);
}
2 changes: 2 additions & 0 deletions frontend/app/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import 'react-native-reanimated';

import { useColorScheme } from '@/hooks/use-color-scheme';

import "../global.css"

export const unstable_settings = {
anchor: '(tabs)',
};
Expand Down
Binary file added frontend/assets/fonts/Roboto-Flex.ttf
Binary file not shown.
9 changes: 9 additions & 0 deletions frontend/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module.exports = function (api) {
api.cache(true);
return {
presets: [
["babel-preset-expo", { jsxImportSource: "nativewind" }],
"nativewind/babel",
],
};
};
28 changes: 28 additions & 0 deletions frontend/components/ui/icons/bookmark.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from "react";
import Svg, { Path } from "react-native-svg";

import type { IconProps } from ".";

import { cssInterop } from "nativewind";

// 1. Tell NativeWind to intercept the `className` prop and apply it as a style to the Svg
cssInterop(Svg, {
className: {
target: "style",
},
});

export default function BookmarkIcon({
color = "#1A2024",
size = 24,
...props
}: IconProps) {
return (
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
<Path
d="M17.25 3H6.75C6.35218 3 5.97064 3.15804 5.68934 3.43934C5.40804 3.72064 5.25 4.10218 5.25 4.5V21C5.25007 21.1338 5.28595 21.2652 5.35393 21.3805C5.42191 21.4958 5.5195 21.5908 5.63659 21.6557C5.75367 21.7206 5.88598 21.7529 6.01978 21.7494C6.15358 21.7458 6.284 21.7066 6.3975 21.6356L12 18.1341L17.6034 21.6356C17.7169 21.7063 17.8472 21.7454 17.9809 21.7488C18.1146 21.7522 18.2467 21.7198 18.3636 21.655C18.4806 21.5902 18.5781 21.4953 18.646 21.3801C18.7139 21.2649 18.7498 21.1337 18.75 21V4.5C18.75 4.10218 18.592 3.72064 18.3107 3.43934C18.0294 3.15804 17.6478 3 17.25 3ZM17.25 19.6472L12.3966 16.6144C12.2774 16.5399 12.1396 16.5004 11.9991 16.5004C11.8585 16.5004 11.7208 16.5399 11.6016 16.6144L6.75 19.6472V4.5H17.25V19.6472Z"
fill="currentColor"
/>
</Svg>
);
}
28 changes: 28 additions & 0 deletions frontend/components/ui/icons/caret-right.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from "react";
import Svg, { Path } from "react-native-svg";

import type { IconProps } from ".";

import { cssInterop } from "nativewind";

// 1. Tell NativeWind to intercept the `className` prop and apply it as a style to the Svg
cssInterop(Svg, {
className: {
target: "style",
},
});

export default function CaretRightIcon({
color = "#1A2024",
size = 24,
...props
}: IconProps) {
return (
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
<Path
d="M17.0306 12.5307L9.53062 20.0307C9.46094 20.1004 9.37821 20.1556 9.28717 20.1933C9.19612 20.2311 9.09854 20.2505 9 20.2505C8.90145 20.2505 8.80387 20.2311 8.71283 20.1933C8.62178 20.1556 8.53905 20.1004 8.46937 20.0307C8.39969 19.961 8.34441 19.8783 8.3067 19.7872C8.26899 19.6962 8.24958 19.5986 8.24958 19.5001C8.24958 19.4015 8.26899 19.3039 8.3067 19.2129C8.34441 19.1218 8.39969 19.0391 8.46937 18.9694L15.4397 12.0001L8.46937 5.03068C8.32864 4.88995 8.24958 4.69907 8.24958 4.50005C8.24958 4.30103 8.32864 4.11016 8.46937 3.96943C8.6101 3.8287 8.80097 3.74963 9 3.74963C9.19902 3.74963 9.38989 3.8287 9.53062 3.96943L17.0306 11.4694C17.1004 11.5391 17.1557 11.6218 17.1934 11.7128C17.2312 11.8039 17.2506 11.9015 17.2506 12.0001C17.2506 12.0986 17.2312 12.1962 17.1934 12.2873C17.1557 12.3783 17.1004 12.461 17.0306 12.5307Z"
fill="currentColor"
/>
</Svg>
);
}
28 changes: 28 additions & 0 deletions frontend/components/ui/icons/clock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from "react";
import Svg, { Path } from "react-native-svg";

import type { IconProps } from ".";

import { cssInterop } from "nativewind";

// 1. Tell NativeWind to intercept the `className` prop and apply it as a style to the Svg
cssInterop(Svg, {
className: {
target: "style",
},
});

export default function ClockIcon({
color = "#1A2024",
size = 24,
...props
}: IconProps) {
return (
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
<Path
d="M12 2.25C10.0716 2.25 8.18657 2.82183 6.58319 3.89317C4.97982 4.96451 3.73013 6.48726 2.99218 8.26884C2.25422 10.0504 2.06114 12.0108 2.43735 13.9021C2.81355 15.7934 3.74215 17.5307 5.10571 18.8943C6.46928 20.2579 8.20656 21.1865 10.0979 21.5627C11.9892 21.9389 13.9496 21.7458 15.7312 21.0078C17.5127 20.2699 19.0355 19.0202 20.1068 17.4168C21.1782 15.8134 21.75 13.9284 21.75 12C21.7473 9.41498 20.7192 6.93661 18.8913 5.10872C17.0634 3.28084 14.585 2.25273 12 2.25ZM12 20.25C10.3683 20.25 8.77326 19.7661 7.41655 18.8596C6.05984 17.9531 5.00242 16.6646 4.378 15.1571C3.75358 13.6496 3.5902 11.9908 3.90853 10.3905C4.22685 8.79016 5.01259 7.32015 6.16637 6.16637C7.32016 5.01259 8.79017 4.22685 10.3905 3.90852C11.9909 3.59019 13.6497 3.75357 15.1571 4.37799C16.6646 5.00242 17.9531 6.05984 18.8596 7.41655C19.7661 8.77325 20.25 10.3683 20.25 12C20.2475 14.1873 19.3775 16.2843 17.8309 17.8309C16.2843 19.3775 14.1873 20.2475 12 20.25ZM18 12C18 12.1989 17.921 12.3897 17.7803 12.5303C17.6397 12.671 17.4489 12.75 17.25 12.75H12C11.8011 12.75 11.6103 12.671 11.4697 12.5303C11.329 12.3897 11.25 12.1989 11.25 12V6.75C11.25 6.55109 11.329 6.36032 11.4697 6.21967C11.6103 6.07902 11.8011 6 12 6C12.1989 6 12.3897 6.07902 12.5303 6.21967C12.671 6.36032 12.75 6.55109 12.75 6.75V11.25H17.25C17.4489 11.25 17.6397 11.329 17.7803 11.4697C17.921 11.6103 18 11.8011 18 12Z"
fill="currentColor"
/>
</Svg>
);
}
12 changes: 12 additions & 0 deletions frontend/components/ui/icons/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { SvgProps } from "react-native-svg";

import ClockIcon from "./clock";
import BookmarkIcon from "./bookmark";
import CaretRightIcon from "./caret-right";
import WalkingIcon from "./walking";
export interface IconProps extends SvgProps {
color?: string;
size?: number;
}

export { ClockIcon, BookmarkIcon, CaretRightIcon, WalkingIcon };
28 changes: 28 additions & 0 deletions frontend/components/ui/icons/walking.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from "react";
import Svg, { Path } from "react-native-svg";

import type { IconProps } from ".";

import { cssInterop } from "nativewind";

// 1. Tell NativeWind to intercept the `className` prop and apply it as a style to the Svg
cssInterop(Svg, {
className: {
target: "style",
},
});

export default function WalkingIcon({
color = "#94A3B8",
size = 14,
...props
}: IconProps) {
return (
<Svg width={size} height={size} viewBox="0 0 14 14" fill="none" {...props}>
<Path
d="M7.875 3.20833C8.51667 3.20833 9.04167 2.68333 9.04167 2.04167C9.04167 1.4 8.51667 0.875 7.875 0.875C7.23333 0.875 6.70833 1.4 6.70833 2.04167C6.70833 2.68333 7.23333 3.20833 7.875 3.20833ZM5.71667 5.19167L4.08333 13.4167H5.30833L6.35833 8.75L7.58333 9.91667V13.4167H8.75V9.04167L7.525 7.875L7.875 6.125C8.63333 7 9.8 7.58333 11.0833 7.58333V6.41667C9.975 6.41667 9.04167 5.83333 8.575 5.01667L7.99167 4.08333C7.75833 3.73333 7.40833 3.5 7 3.5C6.825 3.5 6.70833 3.55833 6.53333 3.55833L3.5 4.84167V7.58333H4.66667V5.6L5.71667 5.19167Z"
fill="currentColor"
/>
</Svg>
);
}
29 changes: 29 additions & 0 deletions frontend/components/ui/open-status.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { View, Text } from "react-native";
import { ClockIcon } from "./icons";

type OpenStatusType = {
openStatus: "open" | "closing" | "closed";
hours?: string[];
};

export default function OpenStatus({ openStatus, hours }: OpenStatusType) {
const color = `text-status-${openStatus}`;
return (
<View className="flex flex-row items-center gap-1">
<Text className="text-status-open text-status-closing text-status-closed hidden">{/** ENSURES COLORS LOAD PROPERLY */}</Text>
<ClockIcon
size={16}
className={`${color}`}
/>
<Text
className={`${color} text-status`}
>
{openStatus === "open" && "Open"}
{openStatus === "closing" && "Closing"}
{openStatus === "closed" && "Closed"}
</Text>
{hours && openStatus !== "closed" && <Text className="text-status text-gray-text">until {hours[1]}</Text>}
{hours && openStatus === "closed" && <Text className="text-status text-gray-text">until {hours[0]}</Text>}
</View>
);
}
71 changes: 71 additions & 0 deletions frontend/components/ui/study-spot-card/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { Image } from "expo-image";

import { View, Text, TouchableOpacity } from "react-native";

import OpenStatus from "../open-status";
import { Tag } from "../tags";

import { BookmarkIcon, CaretRightIcon, WalkingIcon } from "../icons";

const blurhash =
"|rF?hV%2WCj[ayj[a|j[az_NaeWBj@ayfRayfQfQM{M|azj[azf6fQfQfQIpWXofj[ayj[j[fQayWCoeoeaya}j[ayfQa{oLj?j[WVj[ayayj[fQoff7azayj[ayj[j[ayofayayayj[fQj[ayayj[ayfjj[j[ayjuayj[";

type StudySpotCardType = {
locationName: string;
buildingName: string;
location: string; // TODO: get proper type
hours: string[];
tags: string[];
};
export default function StudySpotCard({
locationName,
buildingName,
hours,
tags,
}: StudySpotCardType) {
return (
<View className="flex flex-row gap-2 border p-2 rounded-lg border-card-border">
<View className="w-[78px] aspect-square relative bg-blue-500 rounded">
<Image
source="https://www.rambleratx.com/wp-content/uploads/2022/06/Texas-Union.jpeg"
placeholder={{ blurhash }}
contentFit="cover"
transition={1000}
style={{ width: "100%", height: "100%", borderRadius: 4 }}
/>
<TouchableOpacity
className="bg-saved-bg rounded-full self-start p-1 absolute top-1 left-1"
onPress={() => console.log("Bookmark tapped!")}
hitSlop={{ top: 15, bottom: 15, left: 15, right: 15 }}
>
<BookmarkIcon size={16} className="text-white" />
</TouchableOpacity>
</View>
<View className="flex justify-between">
<View>
<View className="flex flex-row">
<Text className="text-spot-name text-burnt-orange font-bold">
{buildingName}
</Text>
<Text className="text-spot-name font-bold"> {locationName}</Text>
</View>
<View className="flex flex-row items-center gap-3">
<View className="flex flex-row gap-1">
<WalkingIcon size={14} className="text-gray-text" />
<Text className="text-gray-text text-status">0.1 mi</Text>
</View>
<OpenStatus openStatus="open" hours={["5:00am", "5:00pm"]} />
</View>
</View>
<View className="flex flex-row gap-1">
<Tag tag="Lounge" />
<Tag tag="Low Noise" />
<Tag tag="Near Food & Cafe" />
</View>
</View>
<View className="flex justify-center ml-auto">
<CaretRightIcon size={12} className="text-gray-400" />
</View>
</View>
);
}
13 changes: 13 additions & 0 deletions frontend/components/ui/tags/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { View, Text } from "react-native";

type TagProps = {
tag: string;
};

export function Tag({ tag }: TagProps) {
return (
<View className="self-start border border-burnt-orange rounded-full py-1 px-2">
<Text className="text-burnt-orange text-tag w-fit">{tag}</Text>
</View>
);
}
Loading