Skip to content
Merged
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
17 changes: 17 additions & 0 deletions android/app/src/main/res/xml/provider_paths.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Internal app cache directory (for temp files before sharing) -->
<cache-path name="cache" path="." />

<!-- External cache directory (scoped storage) -->
<external-cache-path name="external_cache" path="." />

<!-- Documents directory (Downloads folder) -->
<external-path name="external_files" path="Download/" />

<!-- Internal files directory -->
<files-path name="files" path="." />

<!-- External files directory -->
<external-files-path name="external_app_files" path="." />
</paths>
28 changes: 25 additions & 3 deletions src/layout/navigation/Tabs/CustomTabBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,31 @@ const CustomTabBar: React.FC<CustomTabBarProps> = ({state, navigation}) => {
navigation.navigate('Ai');
};

const handleImportSubjects = () => {
// Navigate to import subjects screen when implemented
console.log('Import Subjects pressed');
const handleImportSubjects = async () => {
try {
const { pickCSVFileRaw } = require('../../../utils/csv-picker');
const { importAndAddToRegisterFromContent } = require('../../../utils/csv-import');

console.log('Import Subjects pressed - starting CSV picker...');

// Pick CSV file and get raw content
const csvContent = await pickCSVFileRaw();

if (csvContent) {
console.log('CSV file selected, starting import...');
// Import and add to current register
await importAndAddToRegisterFromContent(csvContent);
} else {
console.log('No CSV file selected');
}
} catch (error) {
console.error('Error importing subjects:', error);
const { Alert } = require('react-native');
Alert.alert(
'Import Failed',
`Failed to import subjects: ${error instanceof Error ? error.message : 'Unknown error'}`
);
}
};

return (
Expand Down
50 changes: 50 additions & 0 deletions src/screens/user-settings/SettingsScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import Slider from '@react-native-community/slider';
import MultiSelect from 'react-native-multiple-select';
import useStore from '../../store/store';
import { saveScheduleToDevice, shareSchedule } from '../../utils/exportSchedule';
import pickCSVFile, { pickCSVFileRaw } from '../../utils/csv-picker';
import { importAndAddToRegisterFromContent } from '../../utils/csv-import';

// Constants
const packageJson = require('../../../package.json');
Expand All @@ -41,6 +43,7 @@ const SettingsScreen: React.FC = () => {
setSelectedSchedules,
notificationLeadTime,
setNotificationLeadTime,
addMultipleCards,
} = useStore();

const [darkMode, setDarkMode] = useState(true);
Expand Down Expand Up @@ -158,6 +161,40 @@ const SettingsScreen: React.FC = () => {
}
};

const handleImportSchedule = async () => {
try {
console.log('Starting CSV import...');

if (!registers[activeRegister]) {
Alert.alert('Error', 'No active register found. Please create a register first.');
return;
}

// Use raw CSV content instead of parsed data
const csvContent = await pickCSVFileRaw();
console.log('Raw CSV Content received:', csvContent);

if (!csvContent) {
console.log('No CSV content received (user cancelled or error)');
return;
}

// Use the import utility with the current active register
const currentCards = registers[activeRegister]?.cards || [];
await importAndAddToRegisterFromContent(
csvContent,
activeRegister,
currentCards,
addMultipleCards
);

} catch (error) {
console.error('Import schedule error:', error);
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
Alert.alert('Error', `Failed to import schedule: ${errorMessage}`);
}
};

const clearAllData = () => {
Alert.alert(
'Clear All Data',
Expand Down Expand Up @@ -326,6 +363,19 @@ const SettingsScreen: React.FC = () => {
</View>
</View>
</TouchableOpacity>

<TouchableOpacity style={styles.utilityButton} onPress={handleImportSchedule}>
<View style={styles.utilityButtonContent}>
<Image
source={require('../../assets/icons/export.png')}
style={styles.utilityIcon}
/>
<View style={styles.utilityTextContainer}>
<Text style={styles.utilityButtonText}>Import Schedule from CSV</Text>
<Text style={styles.utilityButtonDescription}>Import subjects from a CSV file</Text>
</View>
</View>
</TouchableOpacity>
</View>

{/* Data Management */}
Expand Down
17 changes: 17 additions & 0 deletions src/store/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ interface StoreState {
removeCard: (registerId: number, cardIndex: number) => void;
addAiCard: (registerId: number, aiCard: AiCardInterface) => void;
addMultipleAiCards: (registerId: number, aiCards: AiCardInterface[]) => void;
addMultipleCards: (registerId: number, cards: CardInterface[]) => void;
setRegisterColor: (registerId: number, color: string) => void;
}

Expand Down Expand Up @@ -643,6 +644,22 @@ export const useStore = create<StoreState>()(
};
}),

addMultipleCards: (registerId: number, cards: CardInterface[]) =>
set(state => {
const currentCards = state.registers[registerId]?.cards || [];

return {
registers: {
...state.registers,
[registerId]: {
...state.registers[registerId],
cards: [...currentCards, ...cards],
},
},
updatedAt: new Date(),
};
}),

setRegisterColor: (registerId: number, color: string) =>
set(state => ({
registers: {
Expand Down
38 changes: 38 additions & 0 deletions src/utils/csv-export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export function generateRegisterCSV(registers: { name: string, cards: CardInterf
const register = registers[0];
csv += `${register.name}\n\n`;

// First, add all subjects with their scheduled time slots
dayKeys.forEach((dayKey, i) => {
const subjectRows: string[] = [];
register.cards.forEach(card => {
Expand All @@ -27,7 +28,25 @@ export function generateRegisterCSV(registers: { name: string, cards: CardInterf
csv += subjectRows.join('\n') + '\n\n';
}
});

// Add a section for subjects without any time slots
const subjectsWithoutSlots = register.cards.filter(card => {
return dayKeys.every(dayKey => {
const slots = card.days[dayKey] || [];
return slots.length === 0;
});
});

if (subjectsWithoutSlots.length > 0) {
csv += `Subjects Without Time Slots\n`;
csv += `Subject,Start Time,End Time,Room\n`;
subjectsWithoutSlots.forEach(card => {
csv += `${card.title},Not Scheduled,Not Scheduled,Not Assigned\n`;
});
csv += '\n';
}
} else {
// Handle multiple registers
dayKeys.forEach((dayKey, i) => {
let daySection = `,Day: ${dayNames[i]}\n`;

Expand All @@ -50,6 +69,25 @@ export function generateRegisterCSV(registers: { name: string, cards: CardInterf
csv += daySection;
}
});

// Add sections for subjects without time slots for each register
registers.forEach(register => {
const subjectsWithoutSlots = register.cards.filter(card => {
return dayKeys.every(dayKey => {
const slots = card.days[dayKey] || [];
return slots.length === 0;
});
});

if (subjectsWithoutSlots.length > 0) {
csv += `,${register.name} - Subjects Without Time Slots\n`;
csv += `,Subject,Start Time,End Time,Room\n`;
subjectsWithoutSlots.forEach(card => {
csv += `,${card.title},Not Scheduled,Not Scheduled,Not Assigned\n`;
});
csv += '\n';
}
});
}

return csv;
Expand Down
Loading
Loading