Funktionierende zu und absage.
This commit is contained in:
		
							parent
							
								
									87c0a2d541
								
							
						
					
					
						commit
						43d08a014b
					
				@ -8,14 +8,11 @@ use log::debug;
 | 
			
		||||
use rand::{distributions::Alphanumeric, thread_rng, Rng};
 | 
			
		||||
use serde::Serialize;
 | 
			
		||||
use serde_json::json;
 | 
			
		||||
use terminwahl_typen::{
 | 
			
		||||
    AppointmentSlot, IdType, Nutzer, PlannedAppointment, RequestState, Teacher,
 | 
			
		||||
};
 | 
			
		||||
use terminwahl_typen::{AppointmentSlot, Nutzer, PlannedAppointment, RequestState, Teacher};
 | 
			
		||||
 | 
			
		||||
use crate::db::{
 | 
			
		||||
    self,
 | 
			
		||||
    read::{get_slot_by_id, get_teacher_by_id},
 | 
			
		||||
    write::confirm_appointments,
 | 
			
		||||
    Pool,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -26,7 +23,7 @@ pub struct FullAppointment {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl FullAppointment {
 | 
			
		||||
    async fn get(pool: &Pool, teacher_id: i64, slot_id: i64) -> Result<Self, Box<dyn Error>> {
 | 
			
		||||
    pub async fn get(pool: &Pool, teacher_id: i64, slot_id: i64) -> Result<Self, Box<dyn Error>> {
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            teacher: get_teacher_by_id(pool, teacher_id)
 | 
			
		||||
                .await
 | 
			
		||||
 | 
			
		||||
@ -76,7 +76,7 @@ pub async fn get_unavailable(db: &Pool) -> Result<HashSet<SlotId>, sqlx::Error>
 | 
			
		||||
        SlotId,
 | 
			
		||||
        r#"
 | 
			
		||||
        SELECT teacher_id, slot_id
 | 
			
		||||
        FROM `appointments` WHERE datetime(expires) > datetime('now');"#,
 | 
			
		||||
        FROM `appointments` WHERE datetime(expires) > datetime('now') OR expires is NULL;"#,
 | 
			
		||||
    )
 | 
			
		||||
    .fetch_all(db)
 | 
			
		||||
    .await
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,10 @@
 | 
			
		||||
use chrono::{Duration, Local};
 | 
			
		||||
use sqlx::query;
 | 
			
		||||
use futures::future;
 | 
			
		||||
use sqlx::{query, query_as};
 | 
			
		||||
use terminwahl_typen::{IdType, Nutzer, PlannedAppointment};
 | 
			
		||||
 | 
			
		||||
use crate::api::write::FullAppointment;
 | 
			
		||||
 | 
			
		||||
use super::Pool;
 | 
			
		||||
 | 
			
		||||
pub async fn save_appointments(
 | 
			
		||||
@ -51,12 +54,69 @@ pub async fn save_nutzer(pool: &Pool, nutzer: &Nutzer) -> Result<IdType, sqlx::E
 | 
			
		||||
    Ok(db_nutzer.id)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn confirm_appointments(pool: &Pool, validation_key: &str) -> Result<(), sqlx::Error> {
 | 
			
		||||
pub async fn confirm_appointments(
 | 
			
		||||
    pool: &Pool,
 | 
			
		||||
    validation_key: &str,
 | 
			
		||||
) -> Result<Vec<FullAppointment>, sqlx::Error> {
 | 
			
		||||
    let _ = query!(
 | 
			
		||||
        "UPDATE appointments SET expires = NULL WHERE validation_key = ?",
 | 
			
		||||
        validation_key
 | 
			
		||||
    )
 | 
			
		||||
    .execute(pool)
 | 
			
		||||
    .await?;
 | 
			
		||||
    Ok(())
 | 
			
		||||
    let appointments = query_as!(
 | 
			
		||||
        PlannedAppointment,
 | 
			
		||||
        "Select teacher_id, slot_id from appointments WHERE validation_key = ? and expires is NULL",
 | 
			
		||||
        validation_key
 | 
			
		||||
    )
 | 
			
		||||
    .fetch_all(pool)
 | 
			
		||||
    .await?;
 | 
			
		||||
 | 
			
		||||
    let full_appointments = future::try_join_all(appointments.into_iter().map(
 | 
			
		||||
        |PlannedAppointment {
 | 
			
		||||
             teacher_id,
 | 
			
		||||
             slot_id,
 | 
			
		||||
         }| { FullAppointment::get(pool, teacher_id, slot_id) },
 | 
			
		||||
    ))
 | 
			
		||||
    .await
 | 
			
		||||
    .expect("Failed to get full list of appointments");
 | 
			
		||||
 | 
			
		||||
    Ok(full_appointments)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn cancel_appointment(
 | 
			
		||||
    pool: &Pool,
 | 
			
		||||
    teacher_id: IdType,
 | 
			
		||||
    slot_id: IdType,
 | 
			
		||||
    validation_key: &str,
 | 
			
		||||
) -> Result<Vec<FullAppointment>, sqlx::Error> {
 | 
			
		||||
    let _ = query!(
 | 
			
		||||
        "DELETE FROM appointments WHERE teacher_id = ? AND slot_id = ? AND validation_key = ?",
 | 
			
		||||
        teacher_id,
 | 
			
		||||
        slot_id,
 | 
			
		||||
        validation_key
 | 
			
		||||
    )
 | 
			
		||||
    .execute(pool)
 | 
			
		||||
    .await?;
 | 
			
		||||
 | 
			
		||||
    // Fetch the remaining appointments
 | 
			
		||||
    let appointments = query_as!(
 | 
			
		||||
        PlannedAppointment,
 | 
			
		||||
        "Select teacher_id, slot_id from appointments WHERE validation_key = ? and expires is NULL",
 | 
			
		||||
        validation_key
 | 
			
		||||
    )
 | 
			
		||||
    .fetch_all(pool)
 | 
			
		||||
    .await?;
 | 
			
		||||
 | 
			
		||||
    // Fetch the teacher names and times
 | 
			
		||||
    let full_appointments = future::try_join_all(appointments.into_iter().map(
 | 
			
		||||
        |PlannedAppointment {
 | 
			
		||||
             teacher_id,
 | 
			
		||||
             slot_id,
 | 
			
		||||
         }| { FullAppointment::get(pool, teacher_id, slot_id) },
 | 
			
		||||
    ))
 | 
			
		||||
    .await
 | 
			
		||||
    .expect("Failed to get full list of appointments");
 | 
			
		||||
 | 
			
		||||
    Ok(full_appointments)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2,3 +2,19 @@ pub mod api;
 | 
			
		||||
pub mod db;
 | 
			
		||||
pub mod handlebars_helper;
 | 
			
		||||
pub mod views;
 | 
			
		||||
 | 
			
		||||
pub struct CssPath {
 | 
			
		||||
    pub path: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for CssPath {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        let css_path = glob::glob("terminwahl_front/dist/my_bulma_colors*.css")
 | 
			
		||||
            .expect("Failed to find css file")
 | 
			
		||||
            .next()
 | 
			
		||||
            .expect("Failed to find file")
 | 
			
		||||
            .expect("Failed to find file");
 | 
			
		||||
        let css_file = css_path.file_name().unwrap().to_str().unwrap().to_owned();
 | 
			
		||||
        Self { path: css_file }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -11,7 +11,7 @@ use handlebars::Handlebars;
 | 
			
		||||
use lettre::{transport::smtp::authentication::Credentials, SmtpTransport};
 | 
			
		||||
use log::debug;
 | 
			
		||||
use std::env;
 | 
			
		||||
use terminwahl_back::{api, db, handlebars_helper::TimeOfDate, views};
 | 
			
		||||
use terminwahl_back::{api, db, handlebars_helper::TimeOfDate, views, CssPath};
 | 
			
		||||
 | 
			
		||||
#[actix_web::main]
 | 
			
		||||
async fn main() -> std::io::Result<()> {
 | 
			
		||||
@ -58,6 +58,7 @@ async fn main() -> std::io::Result<()> {
 | 
			
		||||
            .app_data(web::Data::new(pool.clone()))
 | 
			
		||||
            .app_data(web::Data::new(smtp_pool.clone()))
 | 
			
		||||
            .app_data(web::Data::new(handlebars.clone()))
 | 
			
		||||
            .app_data(web::Data::new(CssPath::default()))
 | 
			
		||||
            .wrap(Logger::default())
 | 
			
		||||
            .wrap(session_store)
 | 
			
		||||
            .wrap(error_handlers)
 | 
			
		||||
@ -80,6 +81,10 @@ async fn main() -> std::io::Result<()> {
 | 
			
		||||
                web::resource("/confirm/{validation_key}")
 | 
			
		||||
                    .route(web::get().to(views::confirm_validation_key)),
 | 
			
		||||
            )
 | 
			
		||||
            .service(
 | 
			
		||||
                web::resource("/cancel/{teacher_id}/{slot_id}/{validation_key}")
 | 
			
		||||
                    .route(web::get().to(views::delete_appointment)),
 | 
			
		||||
            )
 | 
			
		||||
            .service(Files::new("/", "./terminwahl_front/dist/").index_file("index.html"))
 | 
			
		||||
    })
 | 
			
		||||
    .bind(("127.0.0.1", 8080))?
 | 
			
		||||
 | 
			
		||||
@ -1,29 +1,54 @@
 | 
			
		||||
use actix_web::{error, web, HttpResponse};
 | 
			
		||||
use glob::glob;
 | 
			
		||||
use handlebars::Handlebars;
 | 
			
		||||
use lettre::SmtpTransport;
 | 
			
		||||
use serde_json::json;
 | 
			
		||||
use terminwahl_typen::RequestState;
 | 
			
		||||
use terminwahl_typen::IdType;
 | 
			
		||||
 | 
			
		||||
use crate::db::{write::confirm_appointments, Pool};
 | 
			
		||||
use crate::{
 | 
			
		||||
    db::{
 | 
			
		||||
        write::{cancel_appointment, confirm_appointments},
 | 
			
		||||
        Pool,
 | 
			
		||||
    },
 | 
			
		||||
    CssPath,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
pub async fn confirm_validation_key(
 | 
			
		||||
    pool: web::Data<Pool>,
 | 
			
		||||
    mailer: web::Data<SmtpTransport>,
 | 
			
		||||
    _mailer: web::Data<SmtpTransport>,
 | 
			
		||||
    css: web::Data<CssPath>,
 | 
			
		||||
    handlebars: web::Data<Handlebars<'_>>,
 | 
			
		||||
    validation_key: web::Path<String>,
 | 
			
		||||
) -> Result<HttpResponse, error::Error> {
 | 
			
		||||
    let css_path = glob("terminwahl_front/dist/my_bulma_colors*.css")
 | 
			
		||||
        .expect("Failed to find css file")
 | 
			
		||||
        .next()
 | 
			
		||||
        .expect("Failed to find file")
 | 
			
		||||
        .expect("Failed to find file");
 | 
			
		||||
    let css_file = css_path.file_name().unwrap().to_str();
 | 
			
		||||
    let data = json!({
 | 
			
		||||
        "css_file" :  css_file,
 | 
			
		||||
    });
 | 
			
		||||
    match confirm_appointments(&pool, &validation_key).await {
 | 
			
		||||
        Ok(_) => Ok(HttpResponse::Ok().body(handlebars.render("confirmed.html", &data).unwrap())),
 | 
			
		||||
        Ok(appointments) => Ok({
 | 
			
		||||
            let data = json!({
 | 
			
		||||
                "css_file" :  css.path,
 | 
			
		||||
                "appointments": appointments,
 | 
			
		||||
                "validation_key": validation_key.into_inner(),
 | 
			
		||||
            });
 | 
			
		||||
            HttpResponse::Ok().body(handlebars.render("confirmed.html", &data).unwrap())
 | 
			
		||||
        }),
 | 
			
		||||
        Err(e) => Err(error::ErrorBadRequest(e)),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn delete_appointment(
 | 
			
		||||
    pool: web::Data<Pool>,
 | 
			
		||||
    _mailer: web::Data<SmtpTransport>,
 | 
			
		||||
    handlebars: web::Data<Handlebars<'_>>,
 | 
			
		||||
    css: web::Data<CssPath>,
 | 
			
		||||
    path: web::Path<(IdType, IdType, String)>,
 | 
			
		||||
) -> Result<HttpResponse, error::Error> {
 | 
			
		||||
    let (teacher_id, slot_id, validation_key) = path.into_inner();
 | 
			
		||||
    match cancel_appointment(&pool, teacher_id, slot_id, &validation_key).await {
 | 
			
		||||
        Ok(appointments) => {
 | 
			
		||||
            let data = json!({
 | 
			
		||||
                "css_file" :  css.path,
 | 
			
		||||
                "appointments": appointments,
 | 
			
		||||
                "validation_key": validation_key,
 | 
			
		||||
            });
 | 
			
		||||
            Ok(HttpResponse::Ok().body(handlebars.render("confirmed.html", &data).unwrap()))
 | 
			
		||||
        }
 | 
			
		||||
        Err(e) => Err(error::ErrorBadRequest(e)),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,25 @@
 | 
			
		||||
  <div class="container">
 | 
			
		||||
    <div class="section">
 | 
			
		||||
      <h1 class="title is-1">Bestätigt</h1>
 | 
			
		||||
      <p>Die unten stehenden Treffen sind bestätigt.</p>
 | 
			
		||||
      <p> Sollten Sie eines oder mehrere Treffen doch nicht wahrnehmen können,
 | 
			
		||||
        sagen Sie diese bitte möglichst früh ab, sodass Sie von anderen wiederum gebucht werden können.</p>
 | 
			
		||||
<div class="mt-6">
 | 
			
		||||
      {{#each appointments as |appoint|}}
 | 
			
		||||
       <div class="columns is-centered"><div class="column is-6">
 | 
			
		||||
         <div class="box"><div class="columns is-vcentered">
 | 
			
		||||
  <div class="column">
 | 
			
		||||
    <b>{{appoint.teacher.ansprache}} {{appoint.teacher.last_name}}: {{time_of appoint.slot.start_time}} - {{time_of appoint.slot.end_time}}</b>
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="column">
 | 
			
		||||
         <a class="button is-small" href="/cancel/{{appoint.teacher.id}}/{{appoint.slot.id}}/{{ ../validation_key }}">
 | 
			
		||||
         
 | 
			
		||||
          <span class="fas fa-ban fa-fw"></span>
 | 
			
		||||
          <span class="ml-2">Dieses Treffen Absagen</span>
 | 
			
		||||
         </a></div></div></div></div></div>
 | 
			
		||||
      
 | 
			
		||||
      {{/each}}
 | 
			
		||||
      </div></div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
@ -1,21 +1,21 @@
 | 
			
		||||
Sehr geehrte/r {{ nutzer.name }},
 | 
			
		||||
 | 
			
		||||
bitte bestätigen Sie durch klick auf den folgenden Link ihre Anmeldung für die Termine
 | 
			
		||||
 | 
			
		||||
Alle Treffen bestätigen:
 | 
			
		||||
https://elternsprechtag.uhle.cloud/confirm/{{ validation_key }}
 | 
			
		||||
 | 
			
		||||
Wenn Sie ein Treffen absagen wollen klicken Sie trotzdem auf den obigen Bestätigungslink und zusätzlich auf den passenden Absage-Link unten.
 | 
			
		||||
Auch bestätigte Treffen können abgesagt werden.
 | 
			
		||||
 | 
			
		||||
Liste der Treffen - mit Absage-Links, sollten Sie zu einem Treffen doch nicht können.
 | 
			
		||||
bitte bestätigen Sie Ihre Anmeldung zu folgenden Terminen durch Anklicken des unten stehenden Links:
 | 
			
		||||
 | 
			
		||||
{{#each appointments as |appoint|}}
 | 
			
		||||
   {{appoint.teacher.ansprache}} {{appoint.teacher.last_name}}: {{time_of appoint.slot.start_time}} - {{time_of appoint.slot.end_time}}
 | 
			
		||||
   Dieses Treffen Absagen:
 | 
			
		||||
        https://elternsprechtag.uhle.cloud/cancel/{{appoint.teacher.id}}/{{appoint.slot.id}}/{{ ../validation_key }}
 | 
			
		||||
 | 
			
		||||
{{/each}}
 | 
			
		||||
 | 
			
		||||
Alle Termine bestätigen:
 | 
			
		||||
https://elternsprechtag.uhle.cloud/confirm/{{ validation_key }}
 | 
			
		||||
 | 
			
		||||
Wenn Sie einen Termin absagen möchten, klicken Sie bitte trotzdem auf den Bestätigungslink oben.
 | 
			
		||||
Dort können Sie dann einzelne oder alle Termine stornieren.
 | 
			
		||||
Dies funktioniert auch, wenn Sie die Termine bereits bestätigt haben.
 | 
			
		||||
 | 
			
		||||
Wenn Sie länger als 3 Stunden gewartet haben, werden die Termine wieder freigegeben.
 | 
			
		||||
Neue Termine müssen Sie wieder über die ursprüngliche Seite buchen.
 | 
			
		||||
 | 
			
		||||
Vielen Dank für Ihre Anmeldung!
 | 
			
		||||
Das Oberstufenkollegium
 | 
			
		||||
@ -161,7 +161,7 @@ impl Component for App {
 | 
			
		||||
          <div class="container">
 | 
			
		||||
            <div class="section">
 | 
			
		||||
                {
 | 
			
		||||
                    if self.nutzer.is_none(){
 | 
			
		||||
                    if let Some(_saved) = self.successfully_saved.as_ref(){self.view_dank_dialog(ctx)} else if self.nutzer.is_none(){
 | 
			
		||||
                        self.view_eingabe_daten(ctx)
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
@ -186,8 +186,8 @@ impl App {
 | 
			
		||||
                <img src="/logoheader.png" />
 | 
			
		||||
              </figure>
 | 
			
		||||
              <div class="box mt-3 is-light">
 | 
			
		||||
                <p>{"Anmeldung zum Elternsprechtag!"}</p><p>{"Bitte geben Sie unbedingt eine valide Emailadresse an,
 | 
			
		||||
                 da die Termine erst nach Bestätigung über den per Email zugesandten Link gebucht sind."}</p>
 | 
			
		||||
                <p>{"Anmeldung zum Elternsprechtag!"}</p><p>{"Bitte geben Sie unbedingt eine gültige E-Mail-Adresse an,
 | 
			
		||||
                 da die Termine erst nach Bestätigung über den per E-Mail zugesandten Link gebucht werden."}</p>
 | 
			
		||||
              </div>
 | 
			
		||||
              <div class="field">
 | 
			
		||||
                <label class="label">{"Voller Name"}</label>
 | 
			
		||||
@ -227,9 +227,9 @@ impl App {
 | 
			
		||||
              </div>
 | 
			
		||||
              <div class="box mt-3 is-light">
 | 
			
		||||
                <h4 class="title is-4">{"Datenschutzerklärung:"}</h4>
 | 
			
		||||
                <p>{"Mit dem klick auf Weiter bestätigen Sie, dass das Lehrerkollegium die hier
 | 
			
		||||
                und im folgenden eingegebenen Daten zur Organisation des Elternsprechtags speichert, verarbeitet und verwendet.
 | 
			
		||||
                Die Daten werden nur für diesen Zweck verwendet."}</p>
 | 
			
		||||
                <p>{"Mit dem Klick auf Weiter bestätigen Sie, dass das Lehrerkollegium die hier
 | 
			
		||||
                und im Folgenden eingegebenen Daten zur Organisation des Elternsprechtages speichert, verarbeitet und nutzt.
 | 
			
		||||
                Die Daten werden ausschließlich zu diesem Zweck verwendet."}</p>
 | 
			
		||||
              <div class="has-text-right mt-6 mr-6">
 | 
			
		||||
                {
 | 
			
		||||
                    if self.tmp_nutzer.validate() {
 | 
			
		||||
@ -278,6 +278,37 @@ impl App {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn view_dank_dialog(&self, _ctx: &Context<Self>) -> Html {
 | 
			
		||||
        html! {<>
 | 
			
		||||
        <div id="app" class="row columns is-multiline">
 | 
			
		||||
            <div class="column is-12">
 | 
			
		||||
            <div class="card">
 | 
			
		||||
                <header class="card-header">
 | 
			
		||||
                <div class="card-header-title is-centered">
 | 
			
		||||
                    {"Erfolgreich abgeschickt, aber noch nicht fertig!"}
 | 
			
		||||
                </div>
 | 
			
		||||
                </header>
 | 
			
		||||
                <div class="card-content">
 | 
			
		||||
                    <div class="content">
 | 
			
		||||
                    <p>{"Sie haben die Termine erfolgreich vorgemerkt. Um sie endgültig zu buchen müssen Sie:"}</p>
 | 
			
		||||
                    <p><ol>
 | 
			
		||||
                        <li>{"In Ihren E-Mails die E-Mail mit dem Betreff \"Elternsprechtag: Bestätigen Sie Ihre Termine\" suchen."}</li>
 | 
			
		||||
                        <li>{"Innerhalb von 3 Stunden auf den darin enthaltenen "}<b>{"Link klicken."}</b></li>
 | 
			
		||||
                    </ol></p>
 | 
			
		||||
 | 
			
		||||
                    <p>{"Sie können auch mehrmals auf den Link klicken, den Sie erhalten haben. Sie können dort Ihre Termine einsehen und gegebenenfalls absagen.
 | 
			
		||||
                        Wenn Sie einen Termin absagen, tun Sie dies bitte so früh wie möglich, damit der Termin wieder für andere frei wird."}</p>
 | 
			
		||||
                    <p>{"Wenn Sie einen anderen Termin buchen möchten, müssen Sie sich erneut über diese Seite anmelden, Sie erhalten dann einen zweiten Link.
 | 
			
		||||
        Achtung: Die Termine, die Sie zuerst gebucht haben, werden dadurch "}<b>{"nicht"}</b>{" gelöscht."}</p>
 | 
			
		||||
                 </div>
 | 
			
		||||
              </div>
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
          </>
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn anleitung(&self) -> Html {
 | 
			
		||||
        html!(            <div class="column is-12">
 | 
			
		||||
            <div class="card">
 | 
			
		||||
@ -289,16 +320,16 @@ impl App {
 | 
			
		||||
                <div class="card-content">
 | 
			
		||||
                  <div class="content">
 | 
			
		||||
                    <ul>
 | 
			
		||||
                        <li>{"Termine sind erst gebucht, wenn Sie:"}
 | 
			
		||||
                        <li>{"Die Termine sind erst gebucht, wenn Sie:"}
 | 
			
		||||
                            <ul><li>{"ganz unten auf Absenden geklickt haben"}</li>
 | 
			
		||||
                                <li>{"Sie die Buchung innerhalb von 3 Stunden über den link in der email bestätigen."}</li>
 | 
			
		||||
                                <li>{"Sie die Buchung innerhalb von 3 Stunden über den Link in der E-Mail bestätigen."}</li>
 | 
			
		||||
                            </ul>
 | 
			
		||||
                        </li>
 | 
			
		||||
                        <li>{"Maximal 3 Termine pro Elternhaus, da die Anzahl der Termine begrenzt ist."}</li>
 | 
			
		||||
                        <li>{"Buchen Sie die Termine so, dass sie zwischen zwei Terminen mindestens einen Slot Pause haben für Raumsuche usw."}</li>
 | 
			
		||||
                        <li>{"Sprechen Sie vor allem mit den LehrerInnen, wo ihre Kinder Probleme haben."}</li>
 | 
			
		||||
                        <li>{"Sollten Sie dringenden Gesprächsbedarf haben, aber alle Termine sind voll, melden Sie sich wie gehabt bei den entsprechenden LehrerInnen"}</li>
 | 
			
		||||
                        <li>{"Sollten Sie ihren Termin nicht wahrnehmen können, sagen Sie ihn möglichst früh ab, dass er erneut belegt werden kann."}</li>
 | 
			
		||||
                        <li>{"Buchen Sie die Termine so, dass Sie zwischen zwei Terminen eine Pause für die Raumsuche haben."}</li>
 | 
			
		||||
                        <li>{"Sprechen Sie vor allem mit den LehrerInnen, deren Fächer Ihren Kindern Probleme bereiten."}</li>
 | 
			
		||||
                        <li>{"Sollten Sie dringenden Gesprächsbedarf haben, aber alle Termine sind voll, melden Sie sich wie gewohnt bei den entsprechenden LehrerInnen"}</li>
 | 
			
		||||
                        <li>{"Wenn Sie Ihren Termin nicht wahrnehmen können, sagen Sie ihn so früh wie möglich ab, damit er neu vergeben werden kann."}</li>
 | 
			
		||||
                        </ul>
 | 
			
		||||
                  </div>
 | 
			
		||||
              </div>
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user