Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixes calendar bug #91

Merged
merged 3 commits into from
Nov 19, 2024
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
14 changes: 2 additions & 12 deletions src/components/Scheduling/AdvancedScheduling.vue
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ const emitSelections = () => {
</script>

<template>
<h2>Photon Ranch</h2>
<h4>Requesting an Observation</h4>
<h2>Request an Observation</h2>

<SchedulingSettings
:show-project-field="true"
Expand All @@ -65,16 +64,7 @@ const emitSelections = () => {
</template>

<style scoped>
h2 {
margin-top: 1em;
}
.input-wrapper {
margin: 1em;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}

.p-text {
margin-right: 1em;
font-size: 1.2em;
Expand Down
10 changes: 7 additions & 3 deletions src/components/Scheduling/Calendar.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup>
import { ref, watch, defineEmits, onMounted } from 'vue'
import { fetchSemesterData, currentSemesterEnd } from '../../utils/calendarUtils'
import { fetchSemesterData, currentSemesterEnd, parseISOString } from '../../utils/calendarUtils'

const emits = defineEmits(['updateDateRange'])

Expand All @@ -25,9 +25,13 @@ onMounted(async () => {
mode="date"
is-range
:min-date="today"
:max-date="new Date(currentSemesterEnd)"
:max-date="parseISOString(currentSemesterEnd)"
placeholder="Select Dates"
is-required
/>
@dayclick="
(_, event) => {
event.target.blur();
}
" />
</div>
</template>
146 changes: 110 additions & 36 deletions src/components/Scheduling/SchedulingSettings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { ref, reactive, computed, defineProps, defineEmits, onMounted } from 'vue'
import { useConfigurationStore } from '../../stores/configuration.js'
import { fetchApiCall } from '../../utils/api.js'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'

const props = defineProps({
showProjectField: {
Expand Down Expand Up @@ -145,10 +146,22 @@ onMounted(() => {
</script>

<template>
<div class="container">
<div class="columns">
<div class="column is-one-third">
<div v-if="showProjectField" class="input-wrapper">
<label for="project-name">Project Name:</label>
<input id="project-name" v-model="projectName" class="scheduling-inputs" placeholder="Enter project name" />

<div class="field is-horizontal">
<div class="field-label is-normal">
<label for="project-name" class="label">Name</label>
</div>
<div class="field-body">
<div class="field">
<div class="control">
<input id="project-name" v-model="projectName" class="scheduling-inputs input" type="text" placeholder="Enter a name for your request" />
</div>
</div>
</div>
</div>
</div>
<!-- Render saved targets and exposures -->
<div v-if="targetList.length > 1">
Expand All @@ -158,50 +171,111 @@ onMounted(() => {
</div>
</div>
</div>

<!-- Target input -->
<div v-if="showTitleField" class="input-wrapper">
<div class="field is-horizontal">
<div class="field-label is-normal">
<label for="target-list" class="label">Target</label>
</div>
<div class="field-body">
<div class="field">
<div class="control">
<input
id="target-list"
v-model="targetList[activeTargetIndex].name"
@blur="getRaDecFromTargetName"
:disabled="!targetEnabled"
:readonly="isTargetConfirmed"
class="scheduling-inputs input"
placeholder="Enter target"
/>
</div>
</div>
</div>
</div>
<p v-if="targetError" class="error-text">{{ targetError }}</p>
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">RA</label>
</div>
<div class="field-body">
<div class="field">
<div class="control">
<input type="text" v-model="targetList[activeTargetIndex].ra" class="scheduling-inputs input" readonly disabled/>
</div>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">Dec</label>
</div>
<div class="field-body">
<div class="field">
<div class="control">
<input type="text" v-model="targetList[activeTargetIndex].dec" class="scheduling-inputs input" readonly disabled/>
</div>
</div>
</div>
</div>
</div>
<v-btn v-if="props.showTitleField" @click="addTarget" color="indigo" :disabled="!addTargetEnabled" class="add-target">Add Another Target</v-btn>
</div>
<div class="column is-one-third">
<!-- Render saved exposures for the active target -->
<div v-if="targetList[activeTargetIndex].exposures.length > 0">
<div>
<div class="highlight-box">
<FontAwesomeIcon icon="fa-regular fa-camera-retro" />
{{ targetList[activeTargetIndex].name || props.target }}: {{ formatExposures(targetList[activeTargetIndex].exposures) }}
</div>
</div>
<!-- Target input -->
<div v-if="showTitleField" class="input-wrapper">
<label for="target-list">Target:</label>
<input
id="target-list"
v-model="targetList[activeTargetIndex].name"
@blur="getRaDecFromTargetName"
:disabled="!targetEnabled"
:readonly="isTargetConfirmed"
class="scheduling-inputs"
placeholder="Enter target"
/>
<p v-if="targetError" class="error-text">{{ targetError }}</p>
<label>RA:</label>
<input type="text" v-model="targetList[activeTargetIndex].ra" class="scheduling-inputs" readonly disabled/>
<label>Dec:</label>
<input type="text" v-model="targetList[activeTargetIndex].dec" class="scheduling-inputs" readonly disabled/>
</div>

<!-- Exposure settings -->
<div class="exposure-settings" :class="{ disabled: !exposureEnabled }">
<div class="select is-fullwidth">
<select id="filter" v-model="settings.filter" :disabled="(!isTargetConfirmed && props.showProjectField) || !exposureEnabled">
<option disabled value="">Choose a filter</option>
<option v-for="filter in filterList" :key="filter.code" :value="filter.code">
{{ filter.name }}
</option>
</select>

<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">Filter</label>
</div>
<div class="field is-horizontal">
<label>Exposure Time</label>
<input type="text" v-model="settings.exposureTime" :disabled="!exposureEnabled" placeholder="Exp time" />
<label>Count</label>
<input type="text" v-model="settings.count" :disabled="!exposureEnabled" placeholder="Count" />
<div class="field-body">
<div class="field is-narrow">
<div class="control" :class="{ disabled: !exposureEnabled }">
<div class="select is-fullwidth">
<select id="filter" v-model="settings.filter" :disabled="(!isTargetConfirmed && props.showProjectField) || !exposureEnabled">
<option disabled value="">Choose a filter</option>
<option v-for="filter in filterList" :key="filter.code" :value="filter.code">
{{ filter.name }}
</option>
</select>
</div>
</div>
</div>
</div>
</div>
<div class="field is-horizontal">
<div class="field-label is-normal">
<label class="label">Exposure</label>
</div>
<div class="field-body">
<div class="field is-narrow">
<p class="control is-expanded">
<input id="exposureTime" type="number" class="input" v-model="settings.exposureTime" :disabled="!exposureEnabled" placeholder="Seconds">
</p>
<p class="help is-danger" v-if="!isExposureTimeValid">{{ exposureError }}</p>
</div>
<div class="times">
<FontAwesomeIcon icon="fa-solid fa-xmark" />
</div>
<div class="field is-narrow">
<p class="control is-expanded">
<input id="exposureCount" type="number" class="input" v-model="settings.count" :disabled="!exposureEnabled" value="1">
</p>
</div>
</div>
</div>
<!-- Add exposure button -->
<v-btn @click="addExposure" color="indigo" :disabled="!addExposuresEnabled" class="add-exposure">Add Exposure</v-btn>
<!-- Add another target button -->
<v-btn v-if="props.showTitleField" @click="addTarget" color="indigo" :disabled="!addTargetEnabled" class="add-target">Add Another Target</v-btn>
</div>
</div>
</template>
4 changes: 3 additions & 1 deletion src/components/Views/SchedulingView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ const enableButton = computed(() => {
</script>

<template>
<section class="section highlight">
<div class="container">
<div v-if="!level && !showScheduled" class="level-buttons-wrapper">
<h2>Submit a Request</h2>
Expand All @@ -155,7 +156,8 @@ const enableButton = computed(() => {
<div v-if="showScheduled">
<ScheduledObservations />
</div>
</div>
</div>
</section>
</template>

<style scoped>
Expand Down
19 changes: 15 additions & 4 deletions src/tests/integration/components/calendar.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,21 @@ import Calendar from '../../../components/Scheduling/Calendar.vue'
import { fetchSemesterData, currentSemesterEnd } from '../../../utils/calendarUtils'
import flushPromises from 'flush-promises'

vi.mock('../../../utils/calendarUtils', () => ({
fetchSemesterData: vi.fn(),
currentSemesterEnd: '2024-08-01T12:00:00Z'
}))
// Partially mock the calendarUtils module
// The terminal suggested using 'importOriginal' to do a partial mock.
// This is necessary because we want to mock some exports (like 'fetchSemesterData' and 'currentSemesterEnd')
// while keeping other exports (like 'parseISOString') unchanged and available.
// 'importOriginal' represents the original module with all its exports.
// Using 'actual' will hold all original exports, then spread them to preserve the original functionality.
vi.mock('../../../utils/calendarUtils', async (importOriginal) => {
const actual = await importOriginal()
return {
// Include all original exports
...actual,
fetchSemesterData: vi.fn(),
currentSemesterEnd: '2024-08-01T12:00:00Z'
}
})

describe('Calendar.vue', () => {
let wrapper
Expand Down
8 changes: 7 additions & 1 deletion src/utils/calendarUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ import { useConfigurationStore } from '../stores/configuration.js'
let currentSemesterStart = null
let currentSemesterEnd = null

function parseISOString (s) {
if (s === null) return null
const b = s.split(/\D+/)
return new Date(Date.UTC(b[0], --b[1], b[2], b[3], b[4], b[5], b[6]))
}

const getStartAndEndDatesOfCurrentSemester = (semesters) => {
const today = new Date()
const currentSemester = semesters.find(semester => {
Expand All @@ -30,4 +36,4 @@ const fetchSemesterData = async () => {
})
}

export { currentSemesterStart, currentSemesterEnd, fetchSemesterData }
export { currentSemesterStart, currentSemesterEnd, fetchSemesterData, parseISOString }
Loading